Last reviewed

Intermediate

Active Directory Lab Setup

Build your own Active Directory environment for practicing domain attacks, Kerberos exploitation, and privilege escalation techniques.

Resource Requirements

An AD lab requires significant resources: at least 16GB RAM for DC + 1 workstation, 32GB+ recommended for multi-machine environments. Use SSDs for better performance.

Isolated Domain Only

This guide creates intentionally weak users, service accounts, AD CS templates, and delegation paths. Build it on an internal or host-only network, snapshot before every vulnerable configuration, and never connect this domain to a production tenant, VPN, or home LAN.

Lab Runbook

Use this page as a controlled lab build, not a production hardening guide. Validate isolation before running exercises and write down the cleanup command before starting.

High risk Intermediate 2-4 hr

Plan

16 GB minimum, 32 GB preferred; 160 GB+. Free eval licenses. Isolation: Internal AD network, optional Kali NAT adapter for updates.

Build

  • - DC and workstation joined
  • - Vulnerable users and SPNs
  • - BloodHound data

Validate

  • - Domain join works
  • - DNS resolves lab.local
  • - BloodHound imports show attack paths

Exercise

Run only the exercises tied to this lab and save screenshots, command output, logs, and timestamps outside disposable VMs.

Clean Up

  • - Revert to pre-attack snapshots
  • - Reset domain user passwords
  • - Archive BloodHound exports outside the VM

Lab Architecture

Basic AD Lab Setup

Required VMs

DC01 - Domain Controller

  • OS: Windows Server 2019/2022
  • RAM: 4GB minimum
  • Disk: 60GB
  • Roles: AD DS, DNS, DHCP (optional)
  • IP: 10.0.0.10 (static)

WS01/WS02 - Workstations

  • OS: Windows 10/11 Pro/Enterprise
  • RAM: 2-4GB each
  • Disk: 40GB each
  • Domain joined: Yes
  • IP: DHCP or static

Step 1: Domain Controller Setup

1.1 Install Windows Server

  1. Create VM with 4GB RAM, 60GB disk
  2. Install Windows Server 2019/2022 (Desktop Experience)
  3. Set static IP: 10.0.0.10, Gateway: 10.0.0.1, DNS: 127.0.0.1
  4. Rename computer to DC01
  5. Keep Windows Firewall enabled where possible; if you temporarily disable it for troubleshooting, snapshot first and re-enable it before continuing.

1.2 Install AD DS Role

powershell
# PowerShell - Install AD DS
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools

# Promote to Domain Controller
Install-ADDSForest -DomainName "lab.local" -DomainNetBIOSName "LAB" -InstallDNS -SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) -Force
# PowerShell - Install AD DS
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools

# Promote to Domain Controller
Install-ADDSForest -DomainName "lab.local" -DomainNetBIOSName "LAB" -InstallDNS -SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) -Force

Server will reboot after promotion completes.

1.3 Create Vulnerable Configuration

powershell
# Create OUs
New-ADOrganizationalUnit -Name "Corp" -Path "DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Users" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Groups" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Computers" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Service Accounts" -Path "OU=Corp,DC=lab,DC=local"

# Create Users with weak passwords (for testing)
$password = ConvertTo-SecureString "LAB_ONLY_Password123!" -AsPlainText -Force

New-ADUser -Name "John Smith" -SamAccountName "jsmith" -UserPrincipalName "jsmith@lab.local" -Path "OU=Users,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true

New-ADUser -Name "Admin User" -SamAccountName "admin.user" -UserPrincipalName "admin.user@lab.local" -Path "OU=Users,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true

# Create a Kerberoastable service account
New-ADUser -Name "SQL Service" -SamAccountName "svc_sql" -UserPrincipalName "svc_sql@lab.local" -Path "OU=Service Accounts,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true -ServicePrincipalNames "MSSQLSvc/sql.lab.local:1433"

# Create AS-REP Roastable user (no pre-auth)
Set-ADAccountControl -Identity "jsmith" -DoesNotRequirePreAuth $true

# Add admin.user to Domain Admins
Add-ADGroupMember -Identity "Domain Admins" -Members "admin.user"
# Create OUs
New-ADOrganizationalUnit -Name "Corp" -Path "DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Users" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Groups" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Computers" -Path "OU=Corp,DC=lab,DC=local"
New-ADOrganizationalUnit -Name "Service Accounts" -Path "OU=Corp,DC=lab,DC=local"

# Create Users with weak passwords (for testing)
$password = ConvertTo-SecureString "LAB_ONLY_Password123!" -AsPlainText -Force

New-ADUser -Name "John Smith" -SamAccountName "jsmith" -UserPrincipalName "jsmith@lab.local" -Path "OU=Users,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true

New-ADUser -Name "Admin User" -SamAccountName "admin.user" -UserPrincipalName "admin.user@lab.local" -Path "OU=Users,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true

# Create a Kerberoastable service account
New-ADUser -Name "SQL Service" -SamAccountName "svc_sql" -UserPrincipalName "svc_sql@lab.local" -Path "OU=Service Accounts,OU=Corp,DC=lab,DC=local" -AccountPassword $password -Enabled $true -ServicePrincipalNames "MSSQLSvc/sql.lab.local:1433"

# Create AS-REP Roastable user (no pre-auth)
Set-ADAccountControl -Identity "jsmith" -DoesNotRequirePreAuth $true

# Add admin.user to Domain Admins
Add-ADGroupMember -Identity "Domain Admins" -Members "admin.user"

Step 2: Workstation Setup

powershell
# Set DNS to DC
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10

# Join domain
Add-Computer -DomainName "lab.local" -Credential LAB\Administrator -Restart

# After restart, add local admin (run as domain admin)
Add-LocalGroupMember -Group "Administrators" -Member "LAB\jsmith"

# Enable WinRM for remote attacks
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
# Set DNS to DC
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10

# Join domain
Add-Computer -DomainName "lab.local" -Credential LAB\Administrator -Restart

# After restart, add local admin (run as domain admin)
Add-LocalGroupMember -Group "Administrators" -Member "LAB\jsmith"

# Enable WinRM for remote attacks
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

Step 3: Add Vulnerable Configurations

Lab Only!

These configurations are intentionally vulnerable. Never apply them to production environments.

Unconstrained Delegation

powershell
# On DC - Enable on WS01
Set-ADComputer -Identity "WS01" -TrustedForDelegation $true
# On DC - Enable on WS01
Set-ADComputer -Identity "WS01" -TrustedForDelegation $true

Weak GPO Permissions

powershell
# Allow jsmith to edit Default Domain Policy
Set-GPPermission -Name "Default Domain Policy" -TargetName "jsmith" -TargetType User -PermissionLevel GpoEdit
# Allow jsmith to edit Default Domain Policy
Set-GPPermission -Name "Default Domain Policy" -TargetName "jsmith" -TargetType User -PermissionLevel GpoEdit

DCSync Rights

powershell
# Grant DCSync to admin.user
Add-ADPermission -Identity "DC=lab,DC=local" -User "admin.user" -Rights ExtendedRight -ExtendedRights "Replicating Directory Changes All"
# Grant DCSync to admin.user
Add-ADPermission -Identity "DC=lab,DC=local" -User "admin.user" -Rights ExtendedRight -ExtendedRights "Replicating Directory Changes All"

LLMNR/NBT-NS Enabled

Leave default - these are enabled by default and vulnerable to poisoning attacks.

AD CS - Vulnerable Certificate Templates (ESC1, ESC4, ESC8)

powershell
# ===== Install AD Certificate Services =====
Install-WindowsFeature ADCS-Cert-Authority -IncludeManagementTools
Install-AdcsCertificationAuthority -CAType EnterpriseRootCA -Force

# Also install the web enrollment service (needed for ESC8)
Install-WindowsFeature ADCS-Web-Enrollment
Install-AdcsWebEnrollment -Force

# ===== ESC1: Enrollee Supplies Subject Alternative Name =====
# Allows any enrolled user to request a cert as any other user (e.g., Domain Admin)
$configNC = (Get-ADRootDSE).configurationNamingContext
$tmplDN = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC"
$userTmpl = Get-ADObject -SearchBase $tmplDN -Filter {cn -eq 'User'}
$esc1 = $userTmpl | Copy-ADObject -TargetPath $tmplDN
Rename-ADObject $esc1 -NewName "VulnESC1"
Set-ADObject $esc1 -Replace @{
  'msPKI-Certificate-Name-Flag' = 1          # ENROLLEE_SUPPLIES_SUBJECT
  'displayName' = 'Vuln ESC1 Template'
}

# ===== ESC4: Vulnerable Template ACLs =====
# Grant Domain Users write access to a certificate template
$esc4 = $userTmpl | Copy-ADObject -TargetPath $tmplDN
Rename-ADObject $esc4 -NewName "VulnESC4"
Set-ADObject $esc4 -Replace @{'displayName' = 'Vuln ESC4 Template'}
$domainUsers = New-Object System.Security.Principal.NTAccount("LAB\Domain Users")
$acl = Get-Acl "AD:\$($esc4.DistinguishedName)"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $domainUsers.Translate([System.Security.Principal.SecurityIdentifier]),
  "GenericAll", "Allow"
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($esc4.DistinguishedName)" $acl

# ===== ESC8: NTLM Relay to Web Enrollment =====
# Web enrollment is already installed above
# By default it accepts NTLM auth, enabling relay attacks
# Validate ESC8 exposure from the AD Attack Paths guide, then revert the snapshot after testing.
# Do not leave Web Enrollment and relayable NTLM paths enabled beyond the exercise.
# ===== Install AD Certificate Services =====
Install-WindowsFeature ADCS-Cert-Authority -IncludeManagementTools
Install-AdcsCertificationAuthority -CAType EnterpriseRootCA -Force

# Also install the web enrollment service (needed for ESC8)
Install-WindowsFeature ADCS-Web-Enrollment
Install-AdcsWebEnrollment -Force

# ===== ESC1: Enrollee Supplies Subject Alternative Name =====
# Allows any enrolled user to request a cert as any other user (e.g., Domain Admin)
$configNC = (Get-ADRootDSE).configurationNamingContext
$tmplDN = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC"
$userTmpl = Get-ADObject -SearchBase $tmplDN -Filter {cn -eq 'User'}
$esc1 = $userTmpl | Copy-ADObject -TargetPath $tmplDN
Rename-ADObject $esc1 -NewName "VulnESC1"
Set-ADObject $esc1 -Replace @{
  'msPKI-Certificate-Name-Flag' = 1          # ENROLLEE_SUPPLIES_SUBJECT
  'displayName' = 'Vuln ESC1 Template'
}

# ===== ESC4: Vulnerable Template ACLs =====
# Grant Domain Users write access to a certificate template
$esc4 = $userTmpl | Copy-ADObject -TargetPath $tmplDN
Rename-ADObject $esc4 -NewName "VulnESC4"
Set-ADObject $esc4 -Replace @{'displayName' = 'Vuln ESC4 Template'}
$domainUsers = New-Object System.Security.Principal.NTAccount("LAB\Domain Users")
$acl = Get-Acl "AD:\$($esc4.DistinguishedName)"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $domainUsers.Translate([System.Security.Principal.SecurityIdentifier]),
  "GenericAll", "Allow"
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($esc4.DistinguishedName)" $acl

# ===== ESC8: NTLM Relay to Web Enrollment =====
# Web enrollment is already installed above
# By default it accepts NTLM auth, enabling relay attacks
# Validate ESC8 exposure from the AD Attack Paths guide, then revert the snapshot after testing.
# Do not leave Web Enrollment and relayable NTLM paths enabled beyond the exercise.

Windows LAPS

powershell
# Windows LAPS is built into modern supported Windows releases.
# Legacy Microsoft LAPS still appears in older labs; prefer Windows LAPS for new builds.
Import-Module LAPS
Update-LapsADSchema

# Delegate LAPS password read to IT Admins
Set-LapsADReadPasswordPermission -Identity "OU=Corp,DC=lab,DC=local" -AllowedPrincipals "IT Admins"

# Allow computers to update their LAPS password
Set-LapsADComputerSelfPermission -Identity "OU=Corp,DC=lab,DC=local"
# Windows LAPS is built into modern supported Windows releases.
# Legacy Microsoft LAPS still appears in older labs; prefer Windows LAPS for new builds.
Import-Module LAPS
Update-LapsADSchema

# Delegate LAPS password read to IT Admins
Set-LapsADReadPasswordPermission -Identity "OU=Corp,DC=lab,DC=local" -AllowedPrincipals "IT Admins"

# Allow computers to update their LAPS password
Set-LapsADComputerSelfPermission -Identity "OU=Corp,DC=lab,DC=local"

Constrained Delegation

powershell
# Configure constrained delegation on svc_sql
# Allows impersonation to specific services
Set-ADUser -Identity "svc_sql" -TrustedForDelegation $false
Set-ADUser -Identity "svc_sql" -Add @{
  'msDS-AllowedToDelegateTo' = @(
    'cifs/DC01.lab.local',
    'cifs/DC01'
  )
}

# Verify the configuration
Get-ADUser svc_sql -Properties msDS-AllowedToDelegateTo | Select-Object -ExpandProperty msDS-AllowedToDelegateTo
# Configure constrained delegation on svc_sql
# Allows impersonation to specific services
Set-ADUser -Identity "svc_sql" -TrustedForDelegation $false
Set-ADUser -Identity "svc_sql" -Add @{
  'msDS-AllowedToDelegateTo' = @(
    'cifs/DC01.lab.local',
    'cifs/DC01'
  )
}

# Verify the configuration
Get-ADUser svc_sql -Properties msDS-AllowedToDelegateTo | Select-Object -ExpandProperty msDS-AllowedToDelegateTo

Resource-Based Constrained Delegation (RBCD)

powershell
# Allow jsmith to configure RBCD on WS01
# This simulates write access to msDS-AllowedToActOnBehalfOfOtherIdentity
$ws01 = Get-ADComputer WS01
$jsmith = Get-ADUser jsmith

# Grant jsmith GenericWrite on WS01 (enables RBCD attack)
$acl = Get-Acl "AD:\$($ws01.DistinguishedName)"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $jsmith.SID, "GenericWrite", "Allow"
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($ws01.DistinguishedName)" $acl
# Allow jsmith to configure RBCD on WS01
# This simulates write access to msDS-AllowedToActOnBehalfOfOtherIdentity
$ws01 = Get-ADComputer WS01
$jsmith = Get-ADUser jsmith

# Grant jsmith GenericWrite on WS01 (enables RBCD attack)
$acl = Get-Acl "AD:\$($ws01.DistinguishedName)"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $jsmith.SID, "GenericWrite", "Allow"
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($ws01.DistinguishedName)" $acl

Shadow Credentials

powershell
# Grant jsmith write access to msDS-KeyCredentialLink on WS01
# This enables Shadow Credentials validation in the AD Attack Paths guide.

# The GenericWrite ACE from RBCD setup also enables this,
# or grant explicit write to the attribute:
$guid = [GUID]"5b47d60f-6090-40b2-9f37-2a4de88f3063"  # msDS-KeyCredentialLink
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $jsmith.SID, "WriteProperty", "Allow", $guid
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($ws01.DistinguishedName)" $acl

# Validate with the Shadow Credentials workflow in /ad-attack-paths/, then revert this snapshot.
# Grant jsmith write access to msDS-KeyCredentialLink on WS01
# This enables Shadow Credentials validation in the AD Attack Paths guide.

# The GenericWrite ACE from RBCD setup also enables this,
# or grant explicit write to the attribute:
$guid = [GUID]"5b47d60f-6090-40b2-9f37-2a4de88f3063"  # msDS-KeyCredentialLink
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
  $jsmith.SID, "WriteProperty", "Allow", $guid
)
$acl.AddAccessRule($ace)
Set-Acl "AD:\$($ws01.DistinguishedName)" $acl

# Validate with the Shadow Credentials workflow in /ad-attack-paths/, then revert this snapshot.

NTLM Relay (Disable SMB Signing)

powershell
# Disable SMB signing on workstations (enabled by default on DCs)
# Run on WS01 and WS02 to enable NTLM relay attacks

# Disable SMB signing requirement
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\LanmanServer\Parameters" -Name "requiresecuritysignature" -Value 0
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\LanmanWorkstation\Parameters" -Name "requiresecuritysignature" -Value 0

# Enable Print Spooler for PrinterBug / PetitPotam coercion
Set-Service -Name Spooler -StartupType Automatic
Start-Service Spooler

# Validate relay exposure only inside the lab, then restore SMB signing and Spooler state.
# Disable SMB signing on workstations (enabled by default on DCs)
# Run on WS01 and WS02 to enable NTLM relay attacks

# Disable SMB signing requirement
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\LanmanServer\Parameters" -Name "requiresecuritysignature" -Value 0
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\LanmanWorkstation\Parameters" -Name "requiresecuritysignature" -Value 0

# Enable Print Spooler for PrinterBug / PetitPotam coercion
Set-Service -Name Spooler -StartupType Automatic
Start-Service Spooler

# Validate relay exposure only inside the lab, then restore SMB signing and Spooler state.

Step 4: Logging with Sysmon

Purple Team Bonus

Adding Sysmon logging lets you practice detection alongside attacks. See what your attacks look like from a defender's perspective.
powershell
# Download Sysmon from Sysinternals
# https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon

# Download SwiftOnSecurity's Sysmon config (recommended baseline)
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" -OutFile sysmonconfig.xml

# Install Sysmon with config (run on DC and workstations)
.Sysmon64.exe -accepteula -i sysmonconfig.xml

# Verify Sysmon is running
Get-Service Sysmon64

# View Sysmon logs in Event Viewer:
# Applications and Services Logs > Microsoft > Windows > Sysmon > Operational
# Download Sysmon from Sysinternals
# https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon

# Download SwiftOnSecurity's Sysmon config (recommended baseline)
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" -OutFile sysmonconfig.xml

# Install Sysmon with config (run on DC and workstations)
.Sysmon64.exe -accepteula -i sysmonconfig.xml

# Verify Sysmon is running
Get-Service Sysmon64

# View Sysmon logs in Event Viewer:
# Applications and Services Logs > Microsoft > Windows > Sysmon > Operational

Automated Lab Builders

DVAD - Deployable Vulnerable AD

Terraform scripts to deploy vulnerable AD labs in cloud or locally.

GitHub →

DVAD Game of Active Directory

Multi-forest AD lab with realistic attack paths.

GitHub →

AutomatedLab

PowerShell module for automated lab deployment with Hyper-V.

automatedlab.org →

Ludus

Automated lab platform by Badsector Labs. Deploy full AD environments on Proxmox.

docs.ludus.cloud →

Troubleshooting FAQ

Workstation won't join the domain
  • Verify DNS points to DC: nslookup lab.local should return DC IP
  • Check connectivity: ping DC01.lab.local
  • Ensure both machines are on the same virtual network segment
  • Verify Windows Firewall isn't blocking — disable it in the lab
Kerberos attacks failing (clock skew)
  • Kerberos requires clocks within 5 minutes — sync Kali: sudo ntpdate DC01.lab.local
  • Or use faketime to adjust tool time: faketime '2026-03-15 10:00:00' impacket-getTGT lab.local/jsmith
  • Set DC as NTP server on all VMs for automatic sync
BloodHound not showing data
  • Verify Neo4j is running: sudo systemctl status neo4j
  • Re-run collection: bloodhound-python -d lab.local -u jsmith -p Password123! -c all
  • Import the JSON files via BloodHound GUI (drag and drop)
  • For BloodHound CE: check Docker containers are running
Windows evaluation license expired
  • Rearm: slmgr /rearm in admin CMD (up to 3 times)
  • Server eval: 180 days × 3 rearms = 720 days total
  • Better approach: use snapshots — revert to pre-expiry state
  • Best approach: use GOAD/Ludus — they handle licensing automatically

Operational Safety Baseline

Apply these rules before running any lab command on this page.

  • Work only on systems you own or have explicit authorization to test.
  • Keep vulnerable services off your home LAN and off public interfaces.
  • Take clean snapshots before every exercise and before every vulnerable configuration change.
  • Use dedicated cloud accounts, subscriptions, and projects with billing alerts before deployment.
  • Write down the teardown command before you run the setup command.

Validation Checkpoints

  • -Domain join works
  • -DNS resolves lab.local
  • -BloodHound imports show attack paths
  • -Snapshot exists before each vulnerable config set

Cleanup And Rollback

  • -Revert to pre-attack snapshots
  • -Reset domain user passwords
  • -Archive BloodHound exports outside the VM