HackBlue

Tools · Endpoint visibility · 05 / 05

Wazuh: watch configuration files and write a local rule

Turn on Wazuh file integrity monitoring for a lab server’s configuration folders, test a custom rule with wazuh-logtest, and read the resulting alert in alerts.json.

12 min read · practice only with authorization

Agent, manager, rules

A Wazuh agent on each host collects logs, file integrity events, and inventory, and sends them to a manager. The manager decodes each event, matches it against rules, and writes alerts above a level threshold. Custom rules live in local_rules.xml and use IDs from 100000 upward so upgrades never overwrite them.

File integrity monitoring

The syscheck block in the agent’s ossec.conf lists folders to watch. realtime uses inotify on Linux for immediate events; whodata adds the user and process that made a change where auditd is available.

Snippet
<syscheck>
  <frequency>43200</frequency>
  <directories check_all="yes" realtime="yes" report_changes="yes">/etc/ssh,/etc/sudoers.d</directories>
  <directories check_all="yes" whodata="yes">/etc/systemd/system</directories>
  <ignore type="sregex">.swp
lt;/ignore> </syscheck> sudo systemctl restart wazuh-agent

A local rule, tested before deployment

This rule raises the level of any integrity change under /etc/sudoers.d. It builds on the stock syscheck rules for modified (550), deleted (553), and added (554) files. wazuh-logtest replays a sample event and shows which rule fired, so the rule can be checked without waiting for a real change.

Snippet
<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="local,syscheck,">
  <rule id="100210" level="12">
    <if_sid>550,553,554</if_sid>
    <field name="file">^/etc/sudoers.d/</field>
    <description>sudoers.d changed on $(agent.name): $(file)</description>
    <mitre><id>T1548.003</id></mitre>
  </rule>
</group>

sudo /var/ossec/bin/wazuh-logtest

Worked case: reading the alert

An admin adds a file to /etc/sudoers.d on lab-web-02. Within seconds the manager writes an alert. whodata shows the change came from a sudo session by the admin account, during a change window recorded in the ticket, so the alert is closed with the ticket number. The same rule would have paged on a change outside a window or from an unexpected account.

Snippet
jq 'select(.rule.id=="100210") | {time: .timestamp, agent: .agent.name, file: .syscheck.path, user: .syscheck.audit.user.name, process: .syscheck.audit.process.name}' /var/ossec/logs/alerts/alerts.json

{"time":"2026-11-02T10:41:07.512+0000","agent":"lab-web-02",
 "file":"/etc/sudoers.d/deploy","user":"ops-amir","process":"/usr/bin/tee"}

Diagram

A file change becomes a Wazuh alert

  1. Watch

    syscheck monitors chosen folders.

    Config
    <directories realtime="yes">/etc/sudoers.d</directories>
  2. Decode

    The manager turns the event into fields.

    Fields
    syscheck.path, syscheck.audit.user.name
  3. Match

    A local rule raises the level for this path.

    Rule
    <rule id="100210" level="12"><if_sid>550,553,554</if_sid>
  4. Review

    Compare the change with the change window.

    Command
    jq 'select(.rule.id=="100210")' alerts.json

Check yourself

1. Why do custom Wazuh rules use IDs of 100000 and above?

2. What does whodata add to an integrity event?

3. How can a new rule be checked without waiting for a real event?