Active Directory

Active Directory Setup

Building the AD root forest on Vultr from scratch — domain controller, OUs, 50+ user accounts, GPOs, AD Certificate Services with a deliberately vulnerable ESC1 template, SMB shares, and Kerberoastable accounts.

Active Directory domain controller setup for hybrid AD lab
Building the AD root forest from scratch on Vultr
Windows Server 2022
AD DS / AD CS
25+ Lab Users
Group Policy
SMB Shares
PKI / CA
Kerberoast Targets
Cloudflare DNS

Step 1 — Buy the Domain & Set Up Cloudflare

Went to Namecheap and bought apex-corp.xyz. A .xyz domain costs about $1–3 a year, which is fine for a lab. Nothing special here — just needed a real public domain to use with Cloudflare DNS, and later for Exchange and the portal.

Namecheap domain purchase showing apex-corp.xyz successfully bought
Namecheap dashboard confirming the apex-corp.xyz domain was purchased

Created a free Cloudflare account, added the domain, and Cloudflare gave two nameservers. Went back into Namecheap, deleted the default nameservers, and replaced them with Cloudflare's ones.

Cloudflare showing apex-corp.xyz domain added and pending nameserver update
Domain added to Cloudflare — waiting for nameserver propagation
Cloudflare showing the two nameserver values to copy into Namecheap
Cloudflare gives the exact nameserver values to paste into Namecheap
Namecheap custom DNS section with Cloudflare nameservers pasted in
Namecheap custom DNS field with the Cloudflare nameservers saved

Used a third-party DNS checker to verify the nameservers had propagated. Takes about 10–30 minutes.

Third party DNS checker confirming Cloudflare nameservers are live for apex-corp.xyz
Third-party checker confirms propagation is done

Once propagated, added DNS A records inside Cloudflare:

TypeNameContentProxy
A@Vultr public IPDNS only (grey cloud)
AhqVultr public IPDNS only (grey cloud)
Cloudflare DNS management page showing A records for apex-corp.xyz pointing at the Vultr VPS IP
Cloudflare DNS records pointing at the DC's public IP — proxy is OFF (grey cloud)
Proxy must be OFF

Any record pointing at an actual server (RDP, WireGuard, SMTP, Active Directory) must have the Cloudflare proxy turned OFF. Orange cloud only forwards HTTP/HTTPS. Everything else — including AD authentication — breaks silently.

Step 2 — Deploy the VPS on Vultr

Logged into Vultr, deployed a new Cloud Compute — Shared CPU instance. Set the hostname to STARK-TOWER and deployed. Specs used:

SettingValue
OSWindows Server 2022 Standard
Plan4 vCPU, 8 GB RAM, 160 GB SSD
RegionEurope (Frankfurt / Amsterdam)
HostnameSTARK-TOWER
Vultr deploy page showing Cloud Compute server type selected
Selecting Cloud Compute server type on Vultr
Vultr plan selection showing 4 vCPU and 8 GB RAM
Plan selected: 4 vCPU / 8 GB RAM
Vultr OS selection showing Windows Server 2022 Standard selected
Windows Server 2022 Standard as the OS

Created a Vultr Firewall Group called apex-lab with these inbound rules:

ProtocolPortSourcePurpose
TCP3389Your home IP onlyRDP (Remote Desktop)
UDP518200.0.0.0/0WireGuard VPN
TCP4430.0.0.0/0HTTPS / AD CS web enrollment
Vultr dashboard showing STARK-TOWER VPS running with its public IP, username and password
Vultr dashboard — machine is running, credentials are shown here

Step 3 — Connect via RDP

Used the Remote Desktop Connection app on the local machine, entered the Vultr public IP, and logged in with the Administrator credentials from the Vultr dashboard.

Windows Remote Desktop Connection dialog with the Vultr VPS IP entered
Connecting to STARK-TOWER via Remote Desktop

Server Manager loaded automatically after login. That is the sign the machine is ready.

Windows Server Manager dashboard visible after RDP login to STARK-TOWER
Server Manager opens automatically — machine is working

Checked disk and RAM to make sure the plan matched what Vultr showed.

Windows system settings showing 157 GB storage on the C drive
Storage confirmed at 157 GB on C drive
Windows Task Manager showing 8 GB RAM installed
Task Manager confirming 8 GB RAM available
Speed test showing approximately 4000 Mbps download on the Vultr VPS
VPS internet speed is around 4000 Mbps — fast enough for everything

Step 4 — Configure DNS and Install AD DS

Opened PowerShell as Administrator. First set DNS to point at itself (localhost) so AD can work. Active Directory needs to resolve its own domain name.

PowerShell (as Admin)
PS> $IDX = (Get-NetAdapter | Where-Object {$_.Status -eq "Up"}).ifIndex PS> Set-DnsClientServerAddress -InterfaceIndex $IDX -ServerAddresses "127.0.0.1","1.1.1.1"
PowerShell showing DNS being set to 127.0.0.1 on the server adapter
DNS pointed at localhost so AD can resolve its own name

Then installed the Active Directory Domain Services role:

PowerShell
PS> Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
PowerShell running Install-WindowsFeature for AD-Domain-Services
Installing AD DS role via PowerShell
PowerShell showing Success output after AD-Domain-Services installation completed
Role installed successfully — no restart needed at this stage

Step 5 — Create the Forest

Ran the command to create the AD forest root domain hq.apex-corp.xyz. The DSRM password is a recovery password you would need if the DC ever lost its AD database — keep it safe. The server reboots automatically after this.

PowerShell
Install-ADDSForest `
  -DomainName "hq.apex-corp.xyz" `
  -DomainNetbiosName "HQ" `
  -ForestMode "WinThreshold" `
  -DomainMode "WinThreshold" `
  -InstallDNS:$true `
  -SafeModeAdministratorPassword (ConvertTo-SecureString "DSRM-P@ss2025!" -AsPlainText -Force) `
  -Force:$true
PowerShell running Install-ADDSForest command to create hq.apex-corp.xyz
Forest creation command running — takes a few minutes
Server showing DCPromo operation completed successfully before rebooting
Promotion completed successfully — server reboots now

After reboot, logged back in via RDP using HQ\Administrator — note the domain prefix is needed now.

RDP login screen showing HQ\Administrator entered as the username
After promotion, log in with domain prefix: HQ\Administrator

Ran Get-ADDomain to verify the domain was live:

PowerShell
PS> Get-ADDomain
PowerShell Get-ADDomain output showing hq.apex-corp.xyz forest and domain details
Get-ADDomain confirms the forest is running at hq.apex-corp.xyz
Server Manager dashboard showing AD DS, DNS, and File Services all green
Server Manager — all roles showing green, everything working

Step 6 — Active Directory Certificate Services (AD CS)

AD CS gives the domain its own Certificate Authority. Certificates it issues are trusted automatically by all domain-joined machines. We also create a deliberately misconfigured certificate template to simulate the ESC1 vulnerability.

PowerShell
# Install the AD CS role and web enrollment
Install-WindowsFeature -Name AD-Certificate, ADCS-Cert-Authority, ADCS-Web-Enrollment, Web-Server -IncludeManagementTools

# Configure an Enterprise Root CA
Install-AdcsCertificationAuthority `
  -CAType EnterpriseRootCA `
  -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" `
  -KeyLength 2048 `
  -HashAlgorithmName SHA256 `
  -CACommonName "Apex-Corp-CA" `
  -ValidityPeriod Years `
  -ValidityPeriodUnits 10 `
  -Force

# Install the web enrollment endpoint
Install-AdcsWebEnrollment -Force
PowerShell running Install-WindowsFeature for AD-Certificate and ADCS-Cert-Authority
Installing the AD CS role features
PowerShell output showing Install-AdcsCertificationAuthority completed successfully
Enterprise Root CA configured — Apex-Corp-CA is now the domain CA

Verified the CA was online with certutil -ping:

CMD
C:\> certutil -ping
certutil -ping output showing Server ICMP Response is active
certutil -ping confirms the CA is responding

Then created the vulnerable certificate template. In certsrv.msc:

  1. Open Certificate Templates Console from certsrv → right-click Certificate Templates → Manage
  2. Duplicate the built-in User template
  3. Name the new template ApexVPN
  4. In the Subject Name tab: select "Supply in the request"
  5. In the Security tab: give Domain Users the Enroll permission
  6. Back in certsrv → Certificate Templates → New → Certificate Template to Issue → select ApexVPN
certsrv.msc showing the ApexVPN template selected in the Certificate Templates to Issue dialog
Publishing the ApexVPN template in certsrv
ApexVPN certificate template properties showing Subject Name tab set to Supply in the request
The critical ESC1 setting — "Supply in the request" on the Subject Name tab
ESC1 Vulnerability

"Supply in the request" means any domain user can put any name they want in the Subject Alternative Name when requesting a certificate — including a Domain Admin's name. This is ESC1 from the Certified Pre-Owned research. With this, a low-privilege user can impersonate DA.

Step 7 — OUs, Groups, and Users

Created Organizational Units to give the domain a realistic company structure, then security groups, and finally around 50 named user accounts pulled from MCU, DCU, and The Boys characters.

PowerShell — create OUs
$base = "DC=hq,DC=apex-corp,DC=xyz"

$ous = @("Executives","IT Department","Security Division","Research",
         "Operations","HR","Finance","DevOps","Service Accounts","Workstations","Servers")

foreach ($ou in $ous) {
    New-ADOrganizationalUnit -Name $ou -Path $base
    Write-Host "Created OU: $ou" -ForegroundColor Green
}
PowerShell showing the one-liner commands to create OUs and groups running in sequence
Creating all OUs and groups in one go via PowerShell
Active Directory Users and Computers showing all OUs created under hq.apex-corp.xyz
All OUs visible in ADUC — company structure is in place
PowerShell — create security groups
$base = "DC=hq,DC=apex-corp,DC=xyz"

"GRP-Executives","GRP-Research","GRP-Operations","GRP-DevOps","GRP-Finance","GRP-HR" | ForEach-Object {
    New-ADGroup -Name $_ -GroupScope Global -GroupCategory Security -Path "OU=Security Division,$base"
}

"IT-Admins","SOC-Team","Linux-SSH-Users","Linux-Sudo-Users" | ForEach-Object {
    New-ADGroup -Name $_ -GroupScope Global -GroupCategory Security -Path "OU=IT Department,$base"
}
Active Directory showing the security groups created under Security Division OU
Security groups created — GRP-Executives, GRP-Operations, IT-Admins, etc.

For users, created characters named after MCU, DCU, and The Boys to make the domain look like a real company. Tony Stark is the main Domain Admin account.

PowerShell — create user accounts
$base = "DC=hq,DC=apex-corp,DC=xyz"
$defaultPass = ConvertTo-SecureString "Apex@2025Secure!" -AsPlainText -Force

# Tony Stark — CTO, Domain Admin
New-ADUser -Name "Tony Stark" -SamAccountName "t.stark" `
  -UserPrincipalName "[email protected]" `
  -Title "CTO" -Department "Executive" `
  -Path "OU=Executives,$base" `
  -AccountPassword $defaultPass -Enabled $true -PasswordNeverExpires $true
Add-ADGroupMember -Identity "Domain Admins" -Members "t.stark"
Add-ADGroupMember -Identity "Enterprise Admins" -Members "t.stark"
Add-ADGroupMember -Identity "Schema Admins" -Members "t.stark"

# Homelander — intentional DA misconfiguration (attack path)
New-ADUser -Name "John Gillman" -SamAccountName "homelander" `
  -UserPrincipalName "[email protected]" `
  -Title "CEO" -Department "Executive" `
  -Path "OU=Executives,$base" `
  -AccountPassword $defaultPass -Enabled $true -PasswordNeverExpires $true
Add-ADGroupMember -Identity "Domain Admins" -Members "homelander"

# Hughie Campbell — standard user, active on FLATIRON workstation
New-ADUser -Name "Hughie Campbell" -SamAccountName "h.campbell" `
  -UserPrincipalName "[email protected]" `
  -Title "Field Operative" -Department "Operations" `
  -Path "OU=Operations,$base" `
  -AccountPassword $defaultPass -Enabled $true -PasswordNeverExpires $true
Add-ADGroupMember -Identity "GRP-Operations" -Members "h.campbell"
Active Directory Users and Computers showing all users and groups organized in their OUs
Full AD structure — users organized across OUs like a real company

Verified which users were in the Domain Admins group:

PowerShell Get-ADGroupMember output showing Domain Admins: Administrator, Tony Stark, and homelander
Domain Admins group — Homelander is in there as the intentional misconfiguration

Step 8 — Service Accounts

Created three service accounts with SPNs registered, making them Kerberoasting targets. These are the accounts an attacker can grab tickets for and crack offline.

PowerShell
$svcOU = "OU=Service Accounts,DC=hq,DC=apex-corp,DC=xyz"

# svc_alfred — Kerberoastable, has GenericAll on Domain Admins group
New-ADUser -Name "svc_alfred" -SamAccountName "svc_alfred" `
  -Description "Backup Service Account" -Path $svcOU `
  -AccountPassword (ConvertTo-SecureString "Alfred#Butler1" -AsPlainText -Force) `
  -Enabled $true -PasswordNeverExpires $true
Set-ADUser -Identity "svc_alfred" -ServicePrincipalNames @{Add="MSSQLSvc/vought-hq.branch.hq.apex-corp.xyz:1433"}

# svc_jarvis — Kerberoastable, credentials planted in Jenkins workspace
New-ADUser -Name "svc_jarvis" -SamAccountName "svc_jarvis" `
  -Description "Web & Database Service" -Path $svcOU `
  -AccountPassword (ConvertTo-SecureString "J@rv1s2025!" -AsPlainText -Force) `
  -Enabled $true -PasswordNeverExpires $true
Set-ADUser -Identity "svc_jarvis" -ServicePrincipalNames @{Add="HTTP/vought-hq.branch.hq.apex-corp.xyz"}

# svc_friday — Kerberoastable, credentials in Jenkins .env file
New-ADUser -Name "svc_friday" -SamAccountName "svc_friday" `
  -Description "Linux Nginx Service Account" -Path $svcOU `
  -AccountPassword (ConvertTo-SecureString "Fr1d@yAI2025" -AsPlainText -Force) `
  -Enabled $true -PasswordNeverExpires $true
Set-ADUser -Identity "svc_friday" -ServicePrincipalNames @{Add="HTTP/batcave.hq.apex-corp.xyz"}

Verify which accounts have SPNs (Kerberoastable):

PowerShell
PS> Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName | Select-Object Name, SamAccountName, ServicePrincipalName

Step 9 — SMB Shares

Created shared folders with dummy sensitive files inside — board meeting notes, VPN configs with passwords, merger documents. The goal is to make shares look like a real company when an attacker lands on the network and starts browsing.

PowerShell
# Create directories
"Executive","Operations","IT","HR" | ForEach-Object {
    New-Item -Path "C:\Shares\$_" -ItemType Directory -Force
}

# Create shares with appropriate permissions
New-SmbShare -Name "Executive$" -Path "C:\Shares\Executive" `
  -FullAccess "HQ\GRP-Executives" -NoAccess "Everyone"

New-SmbShare -Name "Operations" -Path "C:\Shares\Operations" `
  -FullAccess "HQ\GRP-Operations"

New-SmbShare -Name "IT-Tools" -Path "C:\Shares\IT" `
  -FullAccess "HQ\IT-Admins"

New-SmbShare -Name "HR-Records" -Path "C:\Shares\HR" `
  -FullAccess "HQ\GRP-HR" -NoAccess "Everyone"

# List to confirm
Get-SmbShare | Where-Object Name -notlike "*$" | Format-Table Name, Path
PowerShell showing New-SmbShare commands creating Operations, IT-Tools, and HR-Records shares
SMB shares being created via PowerShell
PowerShell Get-SmbShare output listing all active shares including Operations, IT-Tools, HR-Records
All shares visible and active

Step 10 — Group Policy

Created two GPOs — one for password policy and one to enable audit logging for logon events.

PowerShell
# Password policy linked at domain root
New-GPO -Name "APEX-SEC-PasswordPolicy" | New-GPLink -Target "DC=hq,DC=apex-corp,DC=xyz"
Set-ADDefaultDomainPasswordPolicy -Identity "hq.apex-corp.xyz" `
  -MinPasswordLength 8 `
  -ComplexityEnabled $true `
  -LockoutThreshold 5 `
  -LockoutDuration "00:30:00"

# Audit GPO for logon event tracking
New-GPO -Name "ApexSecAuditPolicy" | New-GPLink -Target "DC=hq,DC=apex-corp,DC=xyz"

The audit policy was configured inside Group Policy Management Editor under: Computer Configuration → Windows Settings → Security Settings → Advanced Audit Policy Configuration → Account Logon. Enabled success and failure logging for Kerberos authentication events.

Step 11 — Kerberoastable and AS-REPRoastable Accounts

Some user accounts were configured with "Do not require Kerberos preauthentication" checked. This means an attacker can request a Kerberos AS-REP hash for these accounts without knowing the password — then crack it offline. That is AS-REPRoasting.

PowerShell
PS> Set-ADAccountControl -Identity "some.user" -DoesNotRequirePreAuth $true PS> # Check which accounts are AS-REPRoastable PS> Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth | Select-Object Name, SamAccountName
Active Directory Users and Computers showing the Account tab with Do not require Kerberos preauthentication checked on a user
Setting a user as AS-REPRoastable — the checkbox is in the Account tab
PowerShell showing all users with ServicePrincipalName set — svc_alfred, svc_jarvis, svc_friday
All Kerberoastable accounts — these three service accounts have SPNs registered
Active Directory showing the Attribute Editor tab with servicePrincipalName values for a service account
Verifying the SPN is set correctly on one of the service accounts
Verification — DC01 PowerShell
PS C:\> Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName | Select Name, SamAccountName
Name SamAccountName
---- --------------
svc_alfred svc_alfred
svc_jarvis svc_jarvis
svc_friday svc_friday
PS C:\> Get-ADGroupMember -Identity "Domain Admins" | Select Name
Name
----
Administrator
Tony Stark
homelander
[+] Domain is ready. Forest: hq.apex-corp.xyz | NetBIOS: HQ

Step 12 — Second Domain-Joined Machine

Deployed a second VPS on the same Vultr private network to act as a domain workstation. This is FLATIRON — it runs Windows 10, is domain joined, and has a logged-in session as Hughie Campbell.

Vultr deploy page with an existing VPC network selected for the second machine
Deploying a second VPS into the same Vultr private network
Vultr VPC selection showing the apex-lab VPC being chosen for the new machine
Selecting the existing VPC so both machines share the same private subnet
Vultr VPC dashboard showing both machines and their private IP addresses
VPC overview showing both machines on the same private network
Vultr instances dashboard showing STARK-TOWER and the new FLATIRON machine both running
Vultr dashboard — both machines running

When the machine first booted, pinging the DC failed. The new machine had a link-local address (169.254.x.x) instead of a proper private IP — the network adapter was not configured correctly yet.

PowerShell ping failing because ipconfig shows 169.254.x.x link-local address instead of a proper IP
Ping fails — machine got a link-local address instead of the VPC subnet IP

Fixed by going into Control Panel → Network Connections → IPv4 Properties and manually setting the IP to match the Vultr VPC subnet, with DNS pointed at the DC's private IP.

IPv4 Properties dialog showing static IP set to the Vultr VPC subnet address with DC IP as DNS
Setting a static IP in the VPC range and pointing DNS at the DC

Then joined the domain from Settings → System → About → Domain or workgroup.

Windows Settings showing the Domain join dialog with hq.apex-corp.xyz entered
Joining the domain from Windows Settings
Windows showing Welcome to hq.apex-corp.xyz message after successful domain join
Successful domain join — welcome to hq.apex-corp.xyz

Verified SMB shares were accessible from the new machine by typing \\STARK-TOWER\ in the Run dialog:

Windows Run dialog with \\STARK-TOWER\ entered and SMB share list visible
SMB shares accessible from the workstation — domain trust is working

Logged into the workstation as Hughie Campbell (h.campbell) to make it look like a real employee's machine:

Windows login screen showing h.campbell logging into the FLATIRON workstation
Logged in as Hughie Campbell on the FLATIRON workstation

Also added a wallpaper from The Boys to make the workstation feel realistic.

Windows desktop of FLATIRON showing The Boys cast wallpaper with Hughie and Starlight
FLATIRON workstation — logged in as Hughie, wallpaper from The Boys

Finally confirmed the public domain was pointing to the Vultr IP via Cloudflare. Visiting the domain in a browser hit the IIS default page from the DC — quick visual confirmation the DNS is wired up correctly.

Browser showing IIS default web page when visiting hq.apex-corp.xyz — confirming DNS points to the DC
IIS default page visible at the domain — DNS is working end to end
What we have at this point

Domain hq.apex-corp.xyz running on STARK-TOWER (Vultr, Windows Server 2022). Full AD structure with OUs, groups, ~50 named users. AD CS with the ESC1 vulnerable template. SMB shares with dummy sensitive files. Kerberoastable and AS-REPRoastable accounts configured. A domain-joined workstation (FLATIRON) with an active Hughie Campbell session.

Setup Checklist