Active Directory Lab Setup
Build your own Active Directory environment for practicing domain attacks, Kerberos exploitation, and privilege escalation techniques.
Resource Requirements
Isolated Domain Only
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.
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
- Create VM with 4GB RAM, 60GB disk
- Install Windows Server 2019/2022 (Desktop Experience)
- Set static IP: 10.0.0.10, Gateway: 10.0.0.1, DNS: 127.0.0.1
- Rename computer to DC01
- 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 - 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) -ForceServer will reboot after promotion completes.
1.3 Create Vulnerable Configuration
# 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
# 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 "*" -ForceStep 3: Add Vulnerable Configurations
Lab Only!
Unconstrained Delegation
# On DC - Enable on WS01
Set-ADComputer -Identity "WS01" -TrustedForDelegation $true# On DC - Enable on WS01
Set-ADComputer -Identity "WS01" -TrustedForDelegation $trueWeak GPO Permissions
# 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 GpoEditDCSync Rights
# 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)
# ===== 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
# 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
# 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-AllowedToDelegateToResource-Based Constrained Delegation (RBCD)
# 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)" $aclShadow Credentials
# 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)
# 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
# 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 > OperationalAutomated Lab Builders
DVAD - Deployable Vulnerable AD
Terraform scripts to deploy vulnerable AD labs in cloud or locally.
GitHub →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.localshould 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
faketimeto 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 /rearmin 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