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.

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. Disable Windows Firewall (for lab only!)

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 "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 "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
# Attack: ntlmrelayx -t http://DC01.lab.local/certsrv/certfnsh.asp --adcs --template User
# Use PetitPotam or PrinterBug to coerce authentication to your relay
# ===== 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
# Attack: ntlmrelayx -t http://DC01.lab.local/certsrv/certfnsh.asp --adcs --template User
# Use PetitPotam or PrinterBug to coerce authentication to your relay

LAPS (Local Admin Password Solution)

powershell
# Install LAPS on DC (creates AD schema extension)
# Download LAPS from Microsoft, then:
Import-Module AdmPwd.PS
Update-AdmPwdADSchema

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

# Allow computers to update their LAPS password
Set-AdmPwdComputerSelfPermission -OrgUnit "OU=Corp,DC=lab,DC=local"
# Install LAPS on DC (creates AD schema extension)
# Download LAPS from Microsoft, then:
Import-Module AdmPwd.PS
Update-AdmPwdADSchema

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

# Allow computers to update their LAPS password
Set-AdmPwdComputerSelfPermission -OrgUnit "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 the Shadow Credentials attack (pywhisker / Whisker)

# 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

# Attack: pywhisker -d lab.local -u jsmith -p Password123! --target WS01$ --action add
# Grant jsmith write access to msDS-KeyCredentialLink on WS01
# This enables the Shadow Credentials attack (pywhisker / Whisker)

# 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

# Attack: pywhisker -d lab.local -u jsmith -p Password123! --target WS01$ --action add

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

# Verify: relay with ntlmrelayx
# impacket-ntlmrelayx -tf targets.txt -smb2support
# 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

# Verify: relay with ntlmrelayx
# impacket-ntlmrelayx -tf targets.txt -smb2support

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