---
title: "Building the GOAD Lab: A Two-Forest Active Directory on a Laptop"
description: "The foundation post for the whole series: how a two-forest, three-domain Active Directory lab is provisioned from bare metal on a ThinkPad, what Vagrant and Ansible actually do, the VMware quirks that had to be patched, and how the lab is started, snapshotted, and reached from Kali."
date: 2026-11-02
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/goad-lab-setup/
---

## Why a lab, and why this lab

The four-part "Assessing Active Directory" series starts with a Kali host and zero credentials. This post is the one that comes before it: **how the target was built in the first place.** A single Domain Controller stops teaching you anything after a day, because real Active Directory breaks across trusts, delegation, certificate services, and ACL chains — relationships that only exist when more than one domain is involved. So the lab is [GOAD (Game of Active Directory)](https://orange-cyberdefense.github.io/GOAD/) by Orange Cyberdefense, which packs two forests, three domains, and five Windows servers into one network, along with more than forty deliberate misconfigurations.

Everything below is real: a laptop that is *just* powerful enough, a hypervisor that needed patching, and a provisioning run that took almost five hours.

## The architecture

Two forests and a child domain. `sevenkingdoms.local` is the forest root; `north.sevenkingdoms.local` is its child domain; `essos.local` is a separate forest linked by a cross-forest trust. Three Domain Controllers carry them, two member servers hold the services, and one Linux box does the provisioning.

```
Windows 11 host (VMnet4 · 192.168.56.1)
└── VMware Workstation 26H1
    ├── DC01   kingslanding  192.168.56.10  (WS2019 · sevenkingdoms.local · ADCS)
    ├── DC02   winterfell    192.168.56.11  (WS2019 · north.sevenkingdoms.local)
    ├── DC03   meereen       192.168.56.12  (WS2016 · essos.local)
    ├── SRV02  castelblack   192.168.56.22  (WS2019 · IIS + MSSQL)
    ├── SRV03  braavos       192.168.56.23  (WS2016 · MSSQL · LAPS)
    └── PROVISIONING         192.168.56.3   (Ubuntu 22.04 · Ansible jumpbox)
```

<figure class="diagram">
<svg viewBox="0 0 760 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="GOAD topology with a Windows host, a provisioning jumpbox, three domain controllers across two forests, and two member servers on an isolated network.">
<defs><marker id="goad-setup-arrow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker></defs>
<rect x="12" y="12" width="736" height="226" rx="8" class="dg-box" />
<text x="28" y="36" class="dg-muted">VMNET4 · HOST-ONLY · 192.168.56.0/24 · NO GATEWAY</text>
<rect x="28" y="54" width="132" height="54" rx="6" class="dg-box-accent" /><text x="44" y="77" class="dg-label">Windows host</text><text x="44" y="96" class="dg-mono">VMware + Vagrant</text>
<rect x="28" y="142" width="132" height="54" rx="6" class="dg-box" /><text x="44" y="165" class="dg-label">Jumpbox (.3)</text><text x="44" y="184" class="dg-mono">Ansible</text>
<path d="M94 108 L94 142" class="dg-line" marker-end="url(#goad-setup-arrow)" />
<path d="M160 81 L218 81" class="dg-line" marker-end="url(#goad-setup-arrow)" />
<path d="M160 169 L218 169" class="dg-line" marker-end="url(#goad-setup-arrow)" />
<rect x="218" y="52" width="294" height="146" rx="6" class="dg-box" />
<text x="234" y="74" class="dg-label">sevenkingdoms.local forest</text>
<rect x="234" y="88" width="126" height="40" rx="5" class="dg-node" /><text x="246" y="112" class="dg-mono">DC01 · .10 · AD CS</text>
<rect x="370" y="88" width="126" height="40" rx="5" class="dg-node" /><text x="382" y="112" class="dg-mono">DC02 · .11 · NORTH</text>
<rect x="302" y="144" width="126" height="40" rx="5" class="dg-node" /><text x="314" y="168" class="dg-mono">SRV02 · .22</text>
<path d="M360 108 L370 108" class="dg-line" marker-end="url(#goad-setup-arrow)" />
<rect x="536" y="52" width="196" height="146" rx="6" class="dg-box" />
<text x="552" y="74" class="dg-label">essos.local forest</text>
<rect x="552" y="88" width="164" height="40" rx="5" class="dg-node" /><text x="564" y="112" class="dg-mono">DC03 · .12</text>
<rect x="552" y="144" width="164" height="40" rx="5" class="dg-node" /><text x="564" y="168" class="dg-mono">SRV03 · .23</text>
<path d="M496 108 L552 108" class="dg-line" marker-end="url(#goad-setup-arrow)" />
</svg>
<figcaption>The lab boundary as built: Vagrant creates the machines, the jumpbox configures them, and every target stays on an isolated host-only network.</figcaption>
</figure>

| Domain | NetBIOS | DC | Kind |
| --- | --- | --- | --- |
| `sevenkingdoms.local` | SEVENKINGDOMS | DC01 (.10) | forest root |
| `north.sevenkingdoms.local` | NORTH | DC02 (.11) | child domain |
| `essos.local` | ESSOS | DC03 (.12) | separate forest |

`SRV02` is a `north` member, `SRV03` is an `essos` member, and the certificate authority (`CertSvc`) runs on DC01. Two trusts hold it together: the implicit parent/child trust between `sevenkingdoms` and `north`, and a cross-forest trust to `essos`.

## The host: barely enough

| Component | Value |
| --- | --- |
| Machine | ThinkPad X1 Carbon |
| CPU | Intel Core Ultra 7 268V — 8 cores / 8 threads |
| RAM | 32 GB |
| OS | Windows 11 |

The honest summary is *sufficient, but at the edge.* Six VMs get 12 vCPUs on an 8-core machine (1.5× oversubscribed) and about 20 GB of RAM against 32 GB total. For attacking one target at a time — which is how a pentest is actually run — the idle VMs cost almost nothing. For a parallel Ansible run, the machine crawls. The full install took **4 hours 42 minutes**, and the slowest step was a DC03 reboot that blew past the 600-second Ansible timeout.

| VM | vCPU | RAM |
| --- | --- | --- |
| DC01 / DC02 / DC03 | 2 | 2048 MB |
| SRV02 | 2 | 4096 MB |
| SRV03 | 2 | 3584 MB |
| PROVISIONING | 2 | 2000 MB |

## Provisioning: Vagrant, Ansible, and the jumpbox

GOAD's supported path is **Vagrant** (to create the VMs) plus **Ansible** (to build the domains inside them). On VMware, GOAD uses a *jumpbox* model: Vagrant first boots an Ubuntu box (the `PROVISIONING` machine at `.3`), copies the GOAD source onto it, and then Ansible runs *from the jumpbox* to reach each Windows VM over WinRM and turn it into a domain.

```bash
Vagrant ──► 6 VMs (boxes: windows_2019 ×3, windows_2016 ×2, ubuntu ×1)
   │
   └─► scp GOAD source ──► jumpbox (.3)
                              │
                              └─► ansible-playbook build.yml
                                    ├── dc01 → AD + ADCS + trust
                                    ├── dc02 → child domain
                                    ├── dc03 → second forest
                                    ├── srv02 → IIS + MSSQL
                                    └── srv03 → MSSQL + LAPS
```

The result is a lab that provisions deterministically and can be torn down and rebuilt with one command.

## The parts that had to be patched

The clean description hides the friction. VMware Workstation 26H1 moved to 64-bit, which broke Vagrant's VMware utility in ways that each required a patch:

- **`WOW6432Node` registry fix** — the utility looked for VMware under the 32-bit view and found nothing, so `InstallPath` and `ProductVersion` had to be created under `HKLM\SOFTWARE\WOW6432Node\VMware, Inc.\VMware Workstation`.
- **`VMnet4` created by hand** — Vagrant could not create the network itself (the `vmrest` API returned 400), so the host-only network was created manually in the Virtual Network Editor.
- **`scp` path mangling** — GOAD's copy step passed Windows backslash paths to `scp`, which treated them as remote hosts; it was patched to forward slashes and a `tar + scp` fallback for `rsync`.
- **`fix_ip.ps1` scheduled-task** — applying a static IP killed the WinRM TCP session mid-boot and locked Vagrant, so the IP change was deferred to a scheduled task that runs *after* the session closes.
- **Python 3.14 `configparser`** — comment lines written with `set()` broke the config parser, so the writer was changed to Python comments.

These are the real content of "lab setup." The tooling is mature, but a 64-bit hypervisor transition and a new Python version are enough to break the whole pipeline, and each failure looked like a provisioning bug rather than what it was. The patches are kept as `local-patches.diff` so they survive an upstream `git pull`.

## Networking: host-only on purpose

The lab network is a **host-only** `192.168.56.0/24` on `VMnet4` — no gateway, no internet, DHCP on only for Vagrant's first WinRM contact. The VMs use static addresses and have no route out, which is exactly what you want for an attack lab: it cannot reach the internet, and the internet cannot reach it.

| Setting | Value |
| --- | --- |
| Network | VMware `VMnet4`, host-only |
| Subnet | `192.168.56.0/24` |
| Host IP | `192.168.56.1` |
| Gateway | none |
| DNS (inside lab) | `192.168.56.10` |

The attack host (Kali) reaches it over the LAN: the Windows host forwards between the physical network and `VMnet4` (`IPEnableRouter=1`, forwarding enabled on both interfaces), and the client just needs a route for `192.168.56.0/24`. The rule worth writing down from all this: **keep it host-only and route in — do not bridge `VMnet4` onto a corporate network.**

## Running the lab

```powershell
cd C:\Users\pwc-cyber\GOAD
$env:PATH = 'C:\Program Files\Vagrant\bin;' + $env:PATH
$I = '6c9cb8-goad-vmware'

.\.env\Scripts\python.exe goad.py -t status   -l GOAD -p vmware -i $I
.\.env\Scripts\python.exe goad.py -t start    -l GOAD -p vmware -i $I
.\.env\Scripts\python.exe goad.py -t stop     -l GOAD -p vmware -i $I
.\.env\Scripts\python.exe goad.py -t snapshot -l GOAD -p vmware -i $I
.\.env\Scripts\python.exe goad.py -t reset    -l GOAD -p vmware -i $I
```

The VMs run **headless** — `vmrun list` shows them, but they never appear in the Workstation GUI, and you never close them from the GUI (it desynchronizes Vagrant). A snapshot was taken before any testing began, so a broken lab is a `-t reset` away.

## Credentials worth knowing up front

The lab's entry points — every domain has a known Domain Admin, and the full user/group/ACL catalog lives in one file (`ad/GOAD/data/config.json`) that is the single source of truth:

| Scope | Account | Password |
| --- | --- | --- |
| sevenkingdoms DA | `robert.baratheon` | `iamthekingoftheworld` |
| sevenkingdoms DA | `cersei.lannister` | `il0vejaime` |
| north DA | `eddard.stark` | `FightP3aceAndHonor!` |
| essos DA | `daenerys.targaryen` | `BurnThemAll!` |
| MSSQL SA (SRV02) | `sa` | `Sup1_sa_P@ssw0rd!` |
| MSSQL SA (SRV03) | `sa` | `sa_P@ssw0rd!Ess0s` |

## What this buys

Once it is up, the attack surface is the whole point: Kerberoasting and AS-REP roasting, constrained and unconstrained delegation, eight-hop ACL chains, AD CS ESC1–ESC15, MSSQL linked servers, LAPS, and more — which is exactly what the series that follows walks through, one technique at a time, with the detection and the fix beside each one. That series starts with [GOAD Part 1: From Zero Credentials to the Attack Graph](/posts/opening-an-active-directory-assessment/).

## Evidence and limits

This write-up records one completed GOAD deployment on a ThinkPad X1 Carbon: the six-machine inventory, the `VMnet4` topology, the 4 h 42 min provisioning run, the exact failure points, and the commands used to start, stop, snapshot, and reset the instance. The account names and passwords shown above are GOAD's intentionally vulnerable fixtures, not credentials from another environment.

The result is not a benchmark for every VMware or GOAD release. The registry, `scp`, static-IP, and Python patches describe this Windows 11 / VMware Workstation 26H1 / Python 3.14 combination. A different provider or version may fail elsewhere. The lab remained host-only with no gateway; nothing here was run against a production or third-party network.

## References

- **Orange Cyberdefense, GOAD:** [orange-cyberdefense.github.io/GOAD](https://orange-cyberdefense.github.io/GOAD/)
- **Vagrant:** [vagrantup.com](https://www.vagrantup.com/)
- **Ansible:** [docs.ansible.com](https://docs.ansible.com/)
- **VMware Workstation Pro:** [techdocs.broadcom.com](https://techdocs.broadcom.com/us/en/vmware-cis/desktop-hypervisors/workstation-pro/26H1.html)