Building a Wazuh Lab from Scratch

AnirudhAnirudh
18 min read

In this writeup, I’ll be setting up a wazuh server - An Endpoint detection and response (EDR) tool that monitor devices for activity that could indicate a threat. I’ll be doing that in an Active directory environment containing 1 Domain controller and a client, both a windows server 2019 Virtual machine.

I went with an AD environment mainly because there aren't many tutorials or resources out there that show how to set this up in that context.

This guide assumes that the reader has basic experience working with virtual machines (VMs) and is familiar with general system administration tasks. If you're new to virtualization or managing Windows and Linux environments, I recommend getting comfortable with those concepts before proceeding (Or research about it when you come across something new!)

Setting up the Lab.

I have used the following configuration for the VMs in VIrtual box setup on a Linux Host.

2x Windows server 2019 (64 bit):

  • 2048 MB Memory, 60GB storage, 128 mb VideoRAM - 1 Host only adapter, 1 NAT.

  • Install the VM here - Choose the English 64bit edition.

1x Wazuh All in one server

  • 4096 MB Memory, 20Gb storage, 16mb VideoRAM - 1 Host only adapter

  • Install the Wazuh server here. (Wazuh documentation site)

Once you download the iso and ova file, upload that into VirtualBox or any virtualization tool of your choice with the configuration that i listed above or anything works on your PC without crashing. This is important since we are going to run three vm’s at the same time, therefore i recommend a host with minimum of 16 gb RAM.

**I had faced a virtualization issue - ‘**kvm: failed to initialize KVM: Device or resource busy’ which can be solved by unloading KVM Modules temporarily until reboot -

sudo rmmod kvm_amd && sudo rmmod kvm

Make sure you install desktop evaluation edition for GUI

» Choose the Desktop experience for GUI.

» Wait for the install to get over.

Active directory setup

Now we gotta setup the active directory environment, which is quiet easy. if you don’t know much about AD, you can learn from this well writen blog.

Creating the Domain controller (DC)

Now start both of those windows Virtual machines and set it up with a local admin account.

Open the server manager and click on “Add roles and features”.

  • Click next until Server roles section and tick Active directory domain services.

  • Use default settings until the last sections and click install, wait for the feature to get installed.

After install, you would notice the notification on the top nav bar, click on that and the first notification gives us the option to “Promote to Domain controller”, which is exactly what we want to do.

Choose the Create a new forest radio button and give a Root domain name of your choice. here, i gave mine as ‘redtrib3.in’, but it could be literally anything and doesn’t have to be a valid domain name.

For example give it as: steelmountain.com

Now, go through each steps in the configuration wizard, give it the DC a password. Choose default options for all steps and click Install and wait! Your server might restart.

You have successfully Promoted your Server to a DOMAIN CONTROLLER!

Adding a user to a Domain

Now that we have created a Domain controller in our network, its time for us to add our first (and only) user part of the domain, spin up the other VM if you haven’t already.

Press WIN + R and Type ‘sysdm.cpl‘ to go to system properties, click on the ‘change’ button. choose the Domain option, and add your domain to join (This is case-insensitive and make sure there is no typo). Press OK, Submit Admin credentials and this VM will be part of the domain.

.

You have successfully joined the Domain! if you run into problems, it’s probably related to the DNS, make sure the DC ip is right and pingable from the Client and vice-versa. Make sure both the VM’s are set to Host only network adapter types, also set Static IP on both the VM’s (just in case if the fault is with virtualbox’s DHCP server…).

DNS setup

It is important that every client in an Active directory environment point their DNS to the DC (or an external DNS server that you have setup, its usually the DC that is used as the DNS as well.)

On a client PC part of the domain, do the following:

  • WIN + R —> Type ncpa.cpl

  • Right click on your host only ethernet adapter (its ethernet 2 for me, check by running ipconfig), and click on properties.

  • Authenticate using your DC Admin credentials.

  • From the options double click on ‘Internet Protocol Version 4 (TCP/IPv4)’

  • Point the DNS server to the IP of your Domain Controller (DC)

Setting up Wazuh: Installing agents

Turn on the wazuh all in one VM, set it with host only adapter as we do not need to access the internet but it should be accessible by the agents (windows servers). Make sure all the VMs are in the same IP class and subnet so that we don’t run into network related issues later.

Once the wazuh VM is booted, you should be able to login with the provided credentials. make a note of the wazuh vm IP, which is 192.168.56.105 in my case.

Visit http://<WAZUH-IP>/ to visit the administrator dashboard, use the default credentials -

admin: admin

After login you should see something like this:

Let’s make the DC an Agent.

Click on the deploy new agents button in place of the agents summary. select windows as your operating system.

Set the server address that of the Wazuh server - 192.168.56.105 in my case.

Optionally add an Agent name which will make it easier to recognize the agent, i will name it as client_dc_1.

Copy the powershell command displayed and we are almost ready to deploy our first agent.

Issue: Installing agents in a host only network.


We will run into issue at this stage as the VM is set with host only. To install an agent, we gotta reach out to install a package, which requires an internet connection. But if we set the connection to NAT, all the VMs will have one single IP connected via the host PC’s network, which will make our lab impossible.

This is where i learned that we could setup multiple Network adapters for a VM. We have to do that for every VM which we install as agents in Wazuh.

Shutdown your VM if running.

In virtualbox, right click on a VM > Settings > go to network tab and set adapter 1 as NAT.

in Adapter 2, Tick the enable network adapter option and set it to Host only.

Now go ahead and check the network adapters, boot up the VM, and open cmd, type ipconfig.

You can see two network adapters, the first one is for Host only network with IP of 192.168.56.102, and the second adapter for NAT with ip 10.0.3.15.

To confirm you have Internet connectivity, also try pinging the google’s dns server - 8.8.8.8, or cloudflare’s DNS server - 1.1.1.1

Do the same for every VM’s which you want to install as an agent.


Now that we have successfully setup the network connection, we are ready to make them wazuh agents.

Fire up the Wazuh all in one serve, and Login to the wazuh dashboard, navigate the the agent deployment page - https://<YOUR-WAZUH-IP>/app/endpoints-summary#/agents-preview/deploy

Select windows as the package, set the Wazuh server address. also give it a meaningful agent name so you don’t get confused later.

Follow the instructions and copy the powershell command and paste that into the client and DC PCs.

Do the same for all VMs to be installed as DC.

~ If your copy-paste from host to vm is not working, follow this video to fix it.

If everything went right, you should see two active machines in the endpoint-summary page. (Ignore the disconnected agent in this image).

Integrating sysmon logs

By default, Wazuh uses the windows event logs as a source of logs, which is usually insufficient and really less verbose. Sysmon (system monitor) is a free tool from microsoft that logs detailed information about what’s happening in a more verbose manner.

Integrating sysmon, will give you more visibility into what’s happening.

Unlike normal Windows logs, Sysmon tells you things like:

  • What programs were run

  • What network connections they made

  • What files they created or changed

To download and install sysmon, do the following:

You have installed Sysmon, now to configure Wazuh to collect the sysmon logs, do the following:

  • Open the file: C:\Program Files (x86)\ossec-agent\ossec.conf as administrator.

  • add the following code at the end before </ossec_config> closing tag.

  <!--Sysmon log collection-->
  <localfile>
    <location>Microsoft-Windows-Sysmon/Operational</location>
    <log_format>eventchannel</log_format>
  </localfile>

Reboot the wazuh server.

To verify that if we have successfully added sysmon as a source of logs, go to the wazuh dashboard, under threat intelligence section, Click on threat hunting, click on events in top navbar and you should see a list of events like this:

Click on the inspect icon in the left most end of the event, you should see the data.win.system.channel as Microsoft-Windows-Sysmon/Operational which is what we set as the name of the source in ossec.conf file.

If you cannot find a sysmon event, wait a few minutes for wazuh to collect the events after reboot. or filter for sysmon events using the filter option at the top. Set the Field to ‘data.win.system.channel’, operator to ‘is’, value to 'Microsoft-Windows-Sysmon/Operational’.

Improving logs by enabling Audit policy

Audit Policy is a built-in Windows feature that tells the operating system what security-relevant actions should be logged in the Security Event Log.

By default, Windows is conservative, it logs only the most essential events to avoid performance issues and log bloat. However, this means many useful security events (like failed logins, privilege use, group changes, or policy changes) are not logged unless you explicitly enable them via Audit Policy.

In active directory environments, we can enable audit policies for every computers part of the domain by creating a Group policy object (GPO) and linking it to a Organizational Unit (OU) or the entire domain itself.

Start by pressing Win + R —> type gpmc.msc —> This will take you to the Group Policy Management Console.

Click on Create a GPO in this domain, and Link it here. Give it a name - Wazuh audit policy (or anything).

You have created a empty GPO, move to Linked Group Policy Objects tab and right click on the newly created GPO → Click on Edit.

Follow the below tree to reach Audit policy:

Computer Configuration
   └── Policies
       └── Windows Settings
           └── Security Settings
               └── Local Policies
                   └── Audit Policy

Click on Audit policy and set each of the policy to Log on “Success, failure”. This will log all the events even when a event is success or failure.

To update the GPO quickly use the command:

gpupdate /force

If you want further auditing, checkout Advanced Audit Policy configuration, here i’m setting up an audit policy for kerberos and other logon events:

You have successfully created a GPO to audit events!

The attack

We are finally in the part where we attack and test Wazuh. we are going to try some common attacks against active directory and see how wazuh detects and shows it in its dashboard. if wazuh does not show the alert we are going to find a way to make wazuh detect such kind of attack and test again.

Before we simulate an attack, lets setup the AD to mimic some real world corporate setup so that we dont get bored with one user acccount and a DC:

I created the following users:

  1. scott knowles

  2. terry.colby

  3. elliot.anderson

  4. gideon.goddard

  5. michael.scott

  6. gus.fring

I created the following OU’s and added those users:

  1. Executives - scott.knowles, terry.colby

  2. Network_engineers - elliot.anderson, gideon.goddard

  3. Managers - michael.scott, gus.fring

Creating OU’s and adding users is fairly simple, learn to do that here.

🎯 Attack #1: Bruteforcing Kerberos users


Let’s start with something simple, we will try to enumerate users in kerberos by requesting a TGT(Ticket granting Ticket). I’ll use kerbrute tool for this.

Learn how Kerberos works here.

For this attack, we need a wordlist containing usernames that we want to test, i have quickly made up this wordlist containing usernames which are valid as well as invalid:

brian.griffin
bruce.banner
elliot.a
gideon.g
gus.fring
jan.levinson
jim.halpert
kevin.malone
michael.scott
natasha.romanoff
nick.fury
pam.beasley
ray.donovan
saul.goodman
scott.k
terry.c
tony.stark
wanda.maximoff

Using Kerbrute:

We have found some valid usernames, which if we were to be an attacker would be a really juicy information. But we are defenders this time, so let’s check if wazuh were able to detect such noisy attack.

In the threat intelligence → events section we can notice so many “Windows audit failure event" alerts.

Click on any one of them to see a more detailed view. The detailed view has many information that is interesting to us as a blue teamer. The agent.ip is the ip address of the client that is attacked, and data.win.eventdata.ipAddress is the possibly the source ip. (It is 192.168.56.1 cause i’m using my host machine and not a VM to attack)

The system has a more detailed information in data.win.system.message that says that a TGT ticket was requested for user wanda.maximoff, this can further help us in investigation of this alert.

Wazuh has successfully detected the attack, lets move on.

Bonus: cracking the hash offline:

I used John to crack the hash. (note that user@123 is not a password present in rockyou, i just added it for demonstration — feel free to find one wordlist containing that pass, im sure there is plenty :)

🎯 Attack #2: AS-REP Roasting


AS-REP Roasting targets Active Directory accounts that have Kerberos pre-authentication disabled. In normal Kerberos authentication, pre-auth ensures a user proves their identity before getting a ticket. If it’s disabled, an attacker can request an encrypted authentication response (AS-REP) without needing the user’s password.

This response contains data encrypted with the user’s hash, which the attacker can brute-force offline to recover weak passwords — all without triggering lockouts or alerts.

For this attack, we gotta disable pre-authentication for one of the user accounts.

  • WIN + R —> Type dsa.msc

  • Choose a user from any of the OU (I’m choosing gideon.goddard).

  • Right click the user → Properties → go to Account tab

  • In account options, choose “Do not require Kerberos Preauthentication”.

  • Click apply → OK.

Let’s use impacket tool GetNPUsers to get the encrypted hash that we can crack offline, i used the same users.txt for this.

impacket-GetNPUsers -no-pass -usersfile users.txt -dc-ip 192.168.56.103 REDTRIBE.COM/

Notice that this returned the hash for one single user we set Pre-authentication off to.

How Wazuh reacts:

This detection looks similar to the previous one. it logged that a Kerberos TGT (Ticket Granting Ticket) was requested. It’s important to understand that SIEM tools like Wazuh are log aggregators; they rely entirely on the logs generated by the system. So far, we’ve configured Windows Event Logs, Audit Policy, and Sysmon to generate these logs.

The key limitation is this: logs are only created when the system chooses to log something, often failures or anomalies. If an attack doesn't trigger any error or failure, it may still succeed without raising alerts. That’s what makes AS-REP Roasting especially stealthy. if the attacker uses a valid username with pre-authentication disabled, the system logs it as a normal ticket request, making the attack easy to miss unless you're specifically looking for it.

🎯 Attack #3: Kerberoasting (As a Lateral movement)


Kerberoasting is a post-compromise attack that abuses the way Kerberos handles service accounts in Active Directory. When a user requests access to a service (like SQL Server or IIS), Kerberos issues a Service Ticket (TGS). If the service is registered with a Service Principal Name (SPN), that ticket is encrypted with the service account’s password hash.

If the attacker has access to a regular domain user account (even a low-privilege one), they can request these tickets and extract them — without needing to interact with the service itself.

We already have a low privileged account from the first attack - gideon.g:user@123

Initial setup:

Create a service account and set SPN:

Use the below powershell commands below: make sure you change the FQDN to yours.

New-ADUser -Name "svc_sql" -SamAccountName "svc_sql" -AccountPassword (ConvertTo-SecureString "service@123" -AsPlainText -Force) -Enabled $true
Set-ADUser -Identity svc_sql -ServicePrincipalName "MSSQLSvc/sql.redtribe.com"

Attack:

Let’s start kerberoasting using the impacket tool GetUserSPNs in order to request SPNs from a service account using a low privilege account.

impacket-GetUserSPNs REDTRIBE.COM/gideon.g:user@123 -dc-ip 192.168.56.103 -outputfile roast.txt

We got the SPN hash which we can crack to find the hash:

Wazuh detection:

In wazuh we are hit with an alert which says Successful Remote Logon Detected for the user we kerberoasted. This happens because the impacket tool used NTLM authentication to connect to the domain controller (DC) over SMB or RPC to query SPNs, This triggers an NTLM logon event, logged in Windows as a network logon from a remote IP.

To improve the detection, I created a new rule in local_rules.xml


<group name="security_event, windows,">
<!-- This rule detects Keberoasting attacks using windows security event on the domain controller -->
    <rule id="110002" level="12">
        <if_sid>60103</if_sid>
        <field name="win.system.eventID">^4769$</field>
        <field name="win.eventdata.TicketOptions" type="pcre2">0x40810000</field>
        <field name="win.eventdata.TicketEncryptionType" type="pcre2">0x17</field>
        <options>no_full_log</options>
        <description>Possible Kerberoasting attack - A RC4-HMAC 0x17 typed Service ticket was issued</description>
     </rule>
</group>

This rule will detect Service tickets with RC4-HMAC encryption that was issues. Kerberoasting targets service tickets (TGS) encrypted with RC4-HMAC, because:

  • The RC4 encryption uses the NTLM hash of the service account as the key.

  • This makes it possible to brute-force the password offline using tools like Hashcat or John the Ripper.

🎯 Attack #4: Password spraying

Password spraying is a brute-force attack technique where an attacker tries one or a few common passwords (like Password@123, Welcome1, etc.) across many usernames, rather than hammering one account with many guesses.

How it works:

  • The attacker gathers a list of valid usernames (from AD, LinkedIn, etc.).

  • Tries one common password for all users.

  • Waits a bit (to avoid lockouts or detection), then tries another password.

  • This evades account lockout policies and reduces the chance of detection.

Since there is a chance that at least one of them uses a weak password or a default password, it has higher chance of success. also trying random combinations on different accounts avoid password lockout policies.

Password spraying is hard to detect since username and password combinations are tried from different accounts.

One common pattern you can notice when a user logs in is that two different combinations of events are recorded:

  • Event id 4776 (NTLM Credential validation event) and 4625 (Failed login event) - The Auth has failed

  • Event id 4776 (NTLM Credential validation event) and 4624 (Success login event) - The Auth was successful.

We can create some rules to detect this:

Add the below rules to local_rules.xml which you can access from the navbar > server_management > rules

<!--local_rules.xml-->

<group name="local,">

    <!--Rule 1: Burst of Failed Logons (Event ID 4625)-->
    <rule id="110003" level="10" frequency="5" timeframe="2">
        <if_matched_sid>60122</if_matched_sid>
        <same_field>win.eventdata.ipAddress</same_field>
        <description>Password Spray: Burst of failed logons from same IP</description>
        <options>no_full_log</options>
    </rule>

</group>

<group name="local,">
    <!--Rule 2: Kerberos Pre-auth Failures (Event ID 4771 with Status 0x18)-->
    <rule id="110004" level="3">
      <decoded_as>windows</decoded_as>
      <field name="win.system.eventID">^4771$</field>
      <field name="win.eventdata.status">0x18</field>
      <description>Windows Event: Kerberos pre-authentication failed (code 0x18)</description>
    </rule>

</group>

<group name="local,">
    <rule id="110005" level="10" frequency="2" timeframe="2">
      <if_matched_sid>104391</if_matched_sid>
      <same_field>win.eventdata.ipAddress</same_field>
      <description>Password Spray: Burst of Kerberos pre-auth failures from same IP (2 in 2s)</description>
      <options>no_full_log</options>
    </rule>
</group>

<group name="local,">
    <!--Rule 3: Explicit Credential Logon Attempt (Event ID 4648)-->
    <rule id="110006" level="3">
      <decoded_as>windows</decoded_as>
      <field name="win.system.eventID">^4648$</field>
      <description>Windows Event: Logon attempted with explicit credentials</description>
    </rule>
</group>


<group name="local,">
    <rule id="110007" level="10" frequency="4" timeframe="2">
      <if_matched_sid>104393</if_matched_sid>
      <same_field>win.eventdata.subjectUserName</same_field>
      <description>Suspicious Activity: Burst of explicit credential logons by same user (4 in 2s)</description>
      <options>no_full_log</options>
    </rule>
</group>

<group name="local,">
    <!--Rule 4: Successful Logon After Spray (Event ID 4624 LogonType 3)-->
    <rule id="110008" level="3">
      <decoded_as>windows</decoded_as>
      <field name="win.system.eventID">^4624$</field>
      <field name="win.eventdata.logonType">3</field>
      <description>Windows Event: Successful network logon</description>
    </rule>
</group>

Here is the explanation of each tags and attributes summarised by ChatGPT:

AttributeMeaning
<group name="local,">Groups the rule under a category (comma-separated). Helps in rule management.
<rule id="XXXXX" level="Y">Unique ID and severity level of the rule (1–15, with 10+ indicating high severity).
<decoded_as>windows</decoded_as>Applies this rule only to Windows logs.
<field name="...">value</field>Matches a specific field in the log. <field name="win.system.eventID">^4625$</field> matches Event ID 4625 exactly.
<description>Human-readable description of the alert.
<frequency>Number of log matches needed to trigger the rule.
<timeframe>Time window (in seconds) within which the <frequency> must occur.
<same_field>...</same_field>All matching logs must share this field value (e.g. same IP, user, workstation).
<if_matched_sid>Rule will only trigger if a previous rule with this ID matched. Used to correlate events.
<options>no_full_log</options>Omits full log from alert to reduce noise/log size.
<decoded_as>Specifies the decoder (like windows, json, etc.) for applying the rule.

Save the local_rules.xml file and restart the wazuh manager:

systemctl restart wazuh-manager.service

Now lets do an attack to see this alerts in action:

I used NXC (Next generation crackmapexec) to do a password spray on smb using Names.txt wordlist from Seclists. You can use a list of real users as well, but i did it just to generate some high traffic at once.

Here is the command, use —continue-on-success to keep spraying even if we get a hit.

nxc smb <DC_IP> -u <PATH_TO_WORDLIST> -p 'user@123' --no-bruteforce --continue-on-success

If you check in wazuh you can see alerts popping up everywhere like this:

This validates that our rule for password spray worked!

Conclusion

Setting up this virtual SOC lab was a great hands-on experience. We explored some common attacks on Active Directory and saw how Wazuh can help us catch them in action. Writing our own detection rules made things even more interesting. it showed how much control we have over what we want to monitor. If you're looking to take this further, try adding a Linux machine as an agent and experiment with collecting and analyzing its logs too. There’s a lot more to uncover, and this is just the beginning.

10
Subscribe to my newsletter

Read articles from Anirudh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anirudh
Anirudh

I write about Hacking, CTFs and other interesting security and programming stuff.