Wazuh Deep Dive: File Integrity, Vulnerability Detection and Custom Rules
Part 9 of Tools & Targets: go beyond the Wazuh quickstart with file integrity monitoring, vulnerability detection, CIS-based configuration assessment, custom decoders and rules tested in wazuh-logtest, safe active response, and MITRE ATT&CK mapping.
Part of our free Tools & Targets series. Hands-on guides to the vulnerable practice apps and the security tools you will use on the job. Pair it with our Build Your Own Cyber Lab series for the lab to run them in.
- Vulnerable practice apps: DVWA, bWAPP, Juice Shop, WebGoat, AI Goat and more
- DVWA walkthrough: every module solved at Low, Medium and High
- OWASP Juice Shop: beginner and intermediate challenges solved
- WebGoat and bWAPP: key lessons solved
- Threat modeling your lab with OWASP Threat Dragon
- Nessus Essentials: vulnerability scanning your lab
- Snort and Suricata: network IDS and your first rules
- Splunk in your lab: install, forward Windows logs, search with SPL
- Wazuh deep dive: file integrity, vulnerability detection and custom rules (you are here)
Where we start
This guide builds on home-lab part 9, where you installed Wazuh with the quickstart and enrolled Windows agents with Sysmon. Now you make it earn its keep. Add an Ubuntu agent too (the Ubuntu server from home-lab part 5 is ideal), because several features below are easiest to see on Linux.
Configuration paths and module options change between Wazuh releases. The examples below match current 4.x documentation; check the linked Wazuh docs for your version before editing.
| Where | Configuration file |
|---|---|
| Wazuh server (manager) | /var/ossec/etc/ossec.conf |
| Linux agent | /var/ossec/etc/ossec.conf |
| Windows agent | C:\Program Files (x86)\ossec-agent\ossec.conf |
| Centrally, for a group of agents | agent.conf for that group, edited in the dashboard or on the manager |
Restart the agent or manager after every change, and keep a copy of the original file.
Step 1: File integrity monitoring (FIM)
The FIM module (configured in the <syscheck> section) takes a baseline of file checksums and attributes, then alerts when files are added, modified or deleted. Attackers change config files, drop web shells and plant persistence; FIM catches all three.
Add directories to the Linux agent's <syscheck> block:
<syscheck>
<directories realtime="yes" report_changes="yes">/etc</directories>
<directories realtime="yes">/var/www/html</directories>
<directories>/usr/bin,/usr/sbin</directories>
</syscheck>realtime="yes"alerts as soon as a change happens instead of waiting for the next scheduled scan.report_changes="yes"records what changed in text files - very useful, but do not enable it on directories holding secrets, because the diff ends up in your SIEM.- Directories without
realtimeare checked on the periodic scan (thefrequencysetting).
On Windows, monitor folders such as C:\Users\Public and the Startup folders; the Wazuh docs also show how to watch registry keys used for persistence.
Test it: restart the agent, wait for the baseline scan to finish, then on the Ubuntu agent run echo "lab test" | sudo tee -a /etc/lab-fim-test.conf, edit it again, and delete it. Open the File Integrity Monitoring module in the dashboard and you should see the add, modify and delete events, with the diff for the edit.
Step 2: Vulnerability detection
Wazuh agents inventory installed packages (the syscollector module), and the server matches that inventory against vulnerability feeds. In recent 4.x releases this is configured in the manager's <vulnerability-detection> block and is enabled by default; older releases used a different block, so follow the vulnerability-detection page for your version.
- Confirm the module is enabled on the manager and that agents report their package inventory.
- Open the Vulnerability Detection module in the dashboard. Findings are listed per agent, with CVE, severity, package name and the version that fixes it.
- Pick a finding on the Ubuntu agent, run
sudo apt update && sudo apt upgrade, and watch it clear on the next inventory scan.
Compare the results with the Nessus scan from part 6. Agent-based inventory finds missing patches without network credentials; network scanning finds exposed services and misconfigurations that an inventory never sees. Mature programmes use both.
Step 3: Security configuration assessment (SCA)
SCA checks hosts against hardening policies, many based on CIS Benchmarks, and reports pass or fail per check. It is enabled by default and picks policies that match each agent's operating system.
- Open Configuration Assessment in the dashboard and select the Ubuntu agent.
- Read a failed check: it shows the rationale, the remediation and the exact rule Wazuh evaluated.
- Fix one (for example an SSH setting from home-lab part 5), restart the agent to trigger a new scan, and watch the score improve.
This is the same work as a CIS hardening project at a real employer, on a small scale.
Step 4: Custom decoders and rules
Wazuh processes each event in three phases: pre-decoding (timestamp, hostname, program name), decoding (extracting fields) and rule matching. You add your own in two files on the manager:
/var/ossec/etc/decoders/local_decoder.xml/var/ossec/etc/rules/local_rules.xml
Custom rules use IDs from 100000 upward (the docs give 100000 to 120000) so they never collide with the built-in ruleset.
A decoder for a lab application
Suppose a small lab app writes this to syslog:
Oct 25 10:15:02 web01 labapp[2211]: login failed user=alice src=10.10.10.5<decoder name="labapp">
<program_name>labapp</program_name>
</decoder>
<decoder name="labapp-login">
<parent>labapp</parent>
<regex>login (\S+) user=(\S+) src=(\S+)</regex>
<order>status, user, srcip</order>
</decoder>Rules: a single event, then a correlation
<group name="labapp,">
<rule id="100100" level="5">
<decoded_as>labapp</decoded_as>
<field name="status">failed</field>
<description>labapp: failed login for $(user) from $(srcip)</description>
<group>authentication_failed,</group>
</rule>
<rule id="100101" level="10" frequency="5" timeframe="120">
<if_matched_sid>100100</if_matched_sid>
<same_srcip />
<description>labapp: possible brute force from $(srcip)</description>
<mitre>
<id>T1110</id>
</mitre>
</rule>
</group>level(0-15) sets severity; level 0 means "match but do not alert", which is how you silence noise.fieldmatches a decoded field;matchmatches text anywhere in the log line.if_sidmakes a rule a child of another rule;if_matched_sidwithfrequencyandtimeframecounts earlier matches, here five failures from the same source in 120 seconds.
To tune a noisy built-in rule, create a child with if_sid and a lower level. For example, to quieten SSH authentication failures caused by your own vulnerability scanner (check the built-in rule ID in your dashboard before copying this):
<rule id="100110" level="0">
<if_sid>5716</if_sid>
<srcip>10.10.10.50</srcip>
<description>Ignore SSH failures from the lab vulnerability scanner</description>
</rule>Test with wazuh-logtest before you restart
sudo /var/ossec/bin/wazuh-logtestPaste the sample log line. The output walks through each phase: which decoder matched, which fields it extracted, and which rule fired at which level. Paste it several times to see the frequency rule trigger. When the result is right, restart the manager with sudo systemctl restart wazuh-manager. A typo in an XML file can stop the manager starting, which is why you test first.
Step 5: Active response, carefully
Active response runs a script when a rule fires: for example, adding a firewall block for the source IP of a brute-force attack. The manager's ossec.conf ships with command definitions such as firewall-drop; you then bind a command to rules:
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100101</rules_id>
<timeout>180</timeout>
</active-response>locationlocal runs the response on the agent that reported the event.timeoutremoves the block after 180 seconds, so a mistake heals itself.
Auto-blocking can lock you out. If you trigger a brute-force rule from your own admin workstation, or a rule matches a shared address such as your gateway, you will block yourself or cut the agent off from the manager. Before enabling any block, add your management IPs to the <white_list> entries in the manager's <global> section, always use a timeout, and keep console access to the VM. Test by running a short brute-force burst from Kali against the lab app or SSH on the Ubuntu agent, then confirm the block appears and expires.
Step 6: MITRE ATT&CK mapping
Most built-in rules carry ATT&CK technique IDs, and your own can too (the <mitre> block above). Open the MITRE ATT&CK module in the dashboard to see alerts grouped by tactic and technique. Use it two ways:
- Investigation: click a technique to list every alert behind it, then pivot to the agent and timeframe.
- Coverage review: run your lab attacks from earlier parts and note which techniques produced alerts and which did not. Every blank is a detection to write.
Keep it legal and contained
Only scan, probe or attack machines you own or have written permission to test. Keep vulnerable targets on an isolated lab network with no route to your home LAN or the internet, and never point a scanner or exploit at an address you do not control - even "just to see" is unauthorised access in most jurisdictions.
Next step
This is the last part of the series - you now have a threat model, a scanner, a network IDS and two SIEMs. Go back to part 1, attack a target, and make sure every step you take shows up somewhere. To sharpen detection skills on ready-made scenarios, try our detection engineering lab, EDR lab and threat-hunting lab.
Sources & References
Daniel Agrici
CEH, Security+, PenTest+
Daniel is the founder of CyberCertPrep. With a background in penetration testing and security consulting, he has passed 8 cybersecurity certifications and writes about exam strategies and career development.
Ready to start practicing?
86 certifications. 157,000+ questions. 20 free per cert.