⚡ 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
- 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
- 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) -ForceServer 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 "*" -ForceStep 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 $trueWeak 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 GpoEditDCSync 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 relayLAPS (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-AllowedToDelegateToResource-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)" $aclShadow 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 addNTLM 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 -smb2supportStep 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 > 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