Project Nebula: Detecting with Wazuh
Table of Contents
This is the second part of the DE Lab series, aka Project Nebula. π In the previous post, I covered motivations, topology, tools, and set up the VMs. Now, Iβll walk through setting up Wazuh, configuring Windows and Ubuntu VMs to send logs, and creating a simple detection rule in Wazuh. Let's get started! π
- Detection Lab
- Detecting with Wazuh (you are here)
- Detecting with Elastic
- Debriefing
Wazuh
Wazuh is described by its creators as "a free, open source, and enterprise-ready security monitoring solution for threat detection, integrity monitoring, incident response, and compliance." I decided to try it out because it's gaining popularity and seems to have a solid foundation.
Wazuh is built on the Elastic Stack, but it does an excellent job of abstracting the complexity of the stack through projects like wazuh-docker, a set of Docker Compose files and related documentation that make deploying Wazuh as simple as running a few commands. This is great because it allows me to focus on the lab, rather than the infrastructure.
I followed the official installation guide for version 4.8.2. Below, I show the first steps to deploy a single-node instance. I started downloading the code, and generating the digital certificates that will be used by the application for encrypted communication.
git clone https://github.com/wazuh/wazuh-docker.git -b v4.8.2
cd wazuh-docker/single-node/
docker-compose -f generate-indexer-certs.yml run --rm generator
I encountered a strange error when trying to generate the certificates. After some research, I found out that it's a known bug in Docker Compose. I resolved it by removing the .*credsStore.*
line from ~/.docker/config.json
.
Once the certificates were generated, I built the containers using docker-compose up
, as I wanted to view the deployment logs. After a few moments, the containers were up, and I was able to access the Wazuh dashboard by typing https://localhost
in my browser --Wazuh's port 5601 was mapped to port 443. The default credentials are:
- User:
admin
- Password:
SecretPassword
By default, Wazuh doesn't store all the logs it receives, only those that match detection rules (alerts). This is problematic in my point of view because when investigating incidents, you need to analyze logs --no logs, no crime. According to the official documentation, setting logall
and logall_json
to yes
should make it store all logs. But even after following the full procedure from the documentation, it still didn't work for me. It's probably something I've missed but I didn't want to spend more time on this issue, so I decided to move on. Not the best start.
I wanted to separate Windows and Ubuntu scopes for better management in Wazuh. Here's how I did it:
- Open the hamburger menu in the top-left corner (all options cascade down from it) > Server Management > Endpoint Groups and add two new groups:
windows
andubuntu
. - Edit the
windows
group configuration by replacing theagent.conf
content with the code below to ensure the Windows agent pushes Sysmon logs to Wazuh:
<agent_config>
<!-- Sysmon -->
<localfile>
<location>Microsoft-Windows-Sysmon/Operational</location>
<log_format>eventchannel</log_format>
</localfile>
</agent_config>
Next, I used Wazuh's UI under Server Management > Endpoints Summary to create the installation scripts for the agents. I simply entered the host's IP address (step 2), selected Windows or Linux (DEB aarch64 --remember: it's an ARM-based host) in step 1, and assigned the appropriate group (windows
or ubuntu
) in step 3. I copied each script and saved it in a text file for later use.
It's fine to use the x86_64 version of the agent for Windows (using an ARM-based version here, remember?) since it will run in compatibility mode. However, the Linux agent must match the host's architecture.
Agents
The VMs were previously installed in part 1 of this series. Now it's time to set them up in Wazuh. Starting with Windows (apollo
), I logged in as user neil
(remember the naming convention?), opened PowerShell as Administrator, and ran the following commands to:
- Navigate to the user's Downloads folder.
- Download and install the Sysmon agent.
- Download and install the Wazuh agent using the script generated by Wazuh's UI.
- If you use this, replace
<HOST_IP>
with the host's IP address.
- If you use this, replace
cd $HOME/Downloads
Invoke-WebRequest -Uri https://download.sysinternals.com/files/Sysmon.zip -OutFile .\sysmon.zip
Expand-Archive -Path sysmon.zip -DestinationPath .\sysmon
cd sysmon
.\Sysmon64a.exe -i -accepteula
cd ..
Invoke-WebRequest -Uri https://packages.wazuh.com/4.x/windows/wazuh-agent-4.8.2-1.msi -OutFile ${env.tmp}\wazuh-agent
msiexec.exe /i ${env.tmp}\wazuh-agent /q WAZUH_MANAGER='<HOST_IP>' WAZUH_AGENT_GROUP='windows'
NET START WazuhSvc
Since I'm using an ARM-based system, I used Sysmon64a.exe
. If you're using an x86 CPU, you should use Sysmon64.exe
or Sysmon.exe
. Similarly, the Wazuh agent may be installed in Program Files
, Program Files (x86)
, or Program Files (Arm)
depending on the architecture you're using.
Sysmon logs can be viewed in Windows under Event Viewer > Applications and Services Logs > Microsoft > Windows > Sysmon > Operational.
Now for Ubuntu. Here, I decided to install only the Wazuh agent, leaving other monitoring tools like Auditd and Sysmon for Linux for future additions. I logged into gemini
and ran the following commands to download, install, and start the Wazuh agent --again, replace <HOST_IP>
with the host's IP address:
wget https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.8.2-1_arm64.deb && sudo WAZUH_MANAGER='<HOST_IP>' WAZUH_AGENT_GROUP='ubuntu' dpkg -i ./wazuh-agent_4.8.2-1_arm64.deb
sudo systemctl daemon-reload
sudo systemctl enable wazuh-agent
sudo systemctl start wazuh-agent
For information on troubleshooting Wazuh agent connections, refer to this document.
After that, I logged into the Wazuh dashboard and found both hosts under Server Management > Endpoint Summary. With the agents now being monitored, it was time to have some fun.
Threat Detection
I created a simple rule to test Wazuh's detection engine. The idea was to trigger an alert when a specific process was created, and I randomly chose MS Paint. π¨ In Wazuh, I went to Server Management > Rules > Add new rules file. Named the new rule as 1234-sysmon-test-mspaint-monitor.xml
and added the following content:
<group name="windows, sysmon, test">
<rule id="123456" level="12">
<if_group>sysmon_event1</if_group>
<field name="win.eventdata.image">mspaint.exe</field>
<description>TEST Sysmon: MS Paint process created</description>
</rule>
<rule id="123457" level="12">
<if_group>sysmon_event5</if_group>
<field name="win.eventdata.image">mspaint.exe</field>
<description>TEST Sysmon: MS Paint process terminated</description>
</rule>
</group>
After saving the file and restarting the engine, I opened MS Paint in Windows. Then, I went back to Wazuh under Explore > Discover, and saw the alert appear at the top of the list. I could have also searched for rule.id:123456
.
For a more thorough test, I used Atomic Red Team and ran the T1654. After running the first two commands in Windows (next code listing), two rules were triggered in Wazuh: "Powershell process spawned powershell" and "Powershell.exe spawned a powershell process which executed a base64 encoded command" --yes, Wazuh comes with several detection rules out of the box, all installed and enabled. For the record, the third command did not trigger any rule.
powershell -c {get-eventlog 'Security' | where {$_.Message -like '*SYSTEM*'} | export-csv $env:temp\T1654_events.txt}
powershell -c "remove-item $env:temp\T1654_events.txt -ErrorAction Ignore"
wevtutil enum-logs
The default detections failed for two reasons: (a) simply spawning a process via PowerShell is not inherently malicious and can generate many false positives, and (b) none of the three commands used Base64 encoding. To effectively detect this enumeration, I would monitor commands like /wevtutil (el|enum-logs)/i
and /get-eventlog/i
. Additionally, I'd implement a filter to suppress alerts triggered by trusted admins to reduce false positives. A complementary rule could also be created to monitor cleanup commands (such as wevtutil cl
), which align with T1070.001.
VMs can be suspended due to inactivity or by the user. When this happens, the VM's OS loses track of elapsed time, causing the clock to go out of sync π even if you have NTP enabled. Have in mind that NTP must be forced to sync if the time difference is too large. β οΈ
The time discrepancy between client (VMs) and server (SIEM) can prevent rules from triggering or even block log ingestion. As a rule of thumb, always check the VM's clock before starting a new round of tests to ensure logs have accurate timestamps and rules function as expected.
Time π and timezones πΊοΈ are crucial in Detection Engineering and Incident Response. Yet, they are often overlooked. Don't be that person. π΅οΈββοΈ
To Be Continued
Wazuh is a powerful tool, but it's hard to discuss open-source threat detection without mentioning Elastic. In the next post, I'll set up the Elastic Stack similarly to Wazuh, so I can compare both solutions and choose the one that best suits my needs.
I'm dropping some references in the next section for fellow explorers. Iβll keep exploring Wazuh's capabilities. See you in the next post! π
References
- At Home Detection Engineering Lab for Beginners
- Configure and Deploy Wazuh SIEM with Docker Compose Container
- Wazuh | Part 2 : Installing Wazuh and Configuring the Server
- Hunting with Wazuh: Adding Context
- Wazuh: Mapping Sysmon Events to MITRE ATT&CK IDs
- Writing Custom Rules in Wazuh
- Wazuh Rules Syntax