Files
wazuh/configs
2026-07-31 16:56:01 +00:00
..
2026-07-31 16:44:09 +00:00
2026-05-29 19:02:57 +00:00
2026-07-31 16:56:01 +00:00
2026-07-31 16:56:01 +00:00
2026-05-29 19:02:57 +00:00
2026-07-31 16:43:53 +00:00
2026-07-31 16:44:09 +00:00

Wazuh Custom Rules and Decoders

Custom Wazuh detection content for the wittenberger.us homelab. This folder contains the working local_rules.xml and local_decoder.xml that run on the production Wazuh manager, along with documentation for each.

Files in this folder

File Purpose
local_rules.xml All custom correlation and suppression rules
local_decoder.xml Custom decoders for services shipped decoders don't handle
README.md This file. Overview and deployment procedure
DECODERS.md Detailed reference for every custom decoder
RULES.md Detailed reference for every custom rule, grouped by service

Rule ID range allocation

Rules are numbered in service-specific ranges to prevent collisions when adding new content:

Range Service Notes
100000-100099 SSH Currently only one example rule
100100-100199 Attack / reputation lookups AlienVault IP blacklist correlation
100200-100299 Dovecot Mailcow noise suppression and recon detection
100300-100399 Postfix / mail correlation Brute force, compromise, credential stuffing
100400-100499 Gitea Auth, account lifecycle, SSH keys, tokens
100500-100599 pfSense Currently unused (shipped rules cover baseline)

Rules 87699, 87761, and 87762 are also custom despite being below 100000. These override shipped IDs to enable pfSense wrapped-log correlation.

Deployment procedure

Deploy any change from this folder to the running manager:

sudo cp local_decoder.xml /var/ossec/etc/decoders/
sudo cp local_rules.xml /var/ossec/etc/rules/

sudo /var/ossec/bin/wazuh-analysisd -t
# Verify no errors before restarting

sudo systemctl restart wazuh-manager
sudo tail -20 /var/ossec/logs/ossec.log
# Verify clean startup

If validation fails, do not restart. Fix the XML in this folder, re-copy, re-validate.

Testing procedure

Before deploying, test any new rule against real logs using wazuh-logtest:

sudo /var/ossec/bin/wazuh-logtest

Paste a real log line at the prompt. Verify:

  • Phase 1 extracts program_name correctly
  • Phase 2 shows the expected decoder and all captured fields
  • Phase 3 shows the intended rule firing at the expected level

Test with both malicious and benign log samples. A rule that fires on the malicious case but ALSO fires on legitimate traffic is a false positive generator and must be tuned before deploy.

Custom decoders overview

Two decoders exist because mailcow's Docker log wrapping breaks shipped decoders:

  • mailcow-postfix-sasl-fail — extracts srcip, src_subnet, and dstuser from wrapped Postfix SASL failures
  • mailcow-dovecot-success — extracts srcip from any Dovecot event containing rip=IP,

Additional decoders handle other homelab services:

  • gitea and gitea-auth-fail — parse Gitea log format for auth events
  • pfsense-wrapped and children — parse pfSense filter logs

See DECODERS.md for exact patterns and field extraction details.

Sync workflow

Working state on the manager should always match this repo. Recommended workflow:

Edit in repo first, deploy from repo:

# Edit the rules/decoders in this folder
nano local_rules.xml

# Deploy to manager
sudo cp local_rules.xml /var/ossec/etc/rules/
sudo /var/ossec/bin/wazuh-analysisd -t
sudo systemctl restart wazuh-manager

# Commit
git add local_rules.xml
git commit -m "Add rule 100XXX: description"
git push

If you edited on the manager first, copy back:

sudo cp /var/ossec/etc/rules/local_rules.xml ./
sudo cp /var/ossec/etc/decoders/local_decoder.xml ./
git add local_rules.xml local_decoder.xml
git commit -m "Sync working manager state"
git push

Never edit both places simultaneously; you'll lose changes.

Known suppression rules

Several rules exist purely to reduce alert noise from expected internal traffic:

Rule ID Suppresses
100200 mailcow watchdog auth attempts against watchdog@invalid
100201 Dovecot disconnects from Docker-internal range 172.22.1.x
100202 Chris's own mailbox routine disconnects
100203 mailcow watchdog managesieve health checks
100204 Successful Dovecot logins (needed for correlation, level 0 hides from alerts)
100205 Single Dovecot connections with no auth attempt (port scan primitives)

Rule 100204 is special. It intentionally suppresses ALL successful Dovecot logins from alerting because rule 100311 (compromise correlation) needs 100204 to fire in order to correlate. Without the suppression, every login would alert; with it, only the correlated compromise fires.

Adversary intel

Real credential-stuffing campaign observed against wittenberger.us mailcow:

  • Pattern: Distributed brute force against SMTP submission (587)
  • Timing: Bursts every ~3.5 hours, each lasting 6-10 seconds
  • Source IPs: Rotate every burst, no persistent single-source pattern
  • Target usernames: Common admin defaults (admin, info, contact, postmaster, demo)
  • Detection rules that fire: 100315 (credential stuffing), 100316 (volume)
  • Rules that don't fire: 100310, 100313 (single-source rules) - because IPs rotate

Rules 100312-100316 were built specifically to catch this pattern after 100310 alone proved insufficient.

Lessons applicable to CyberForce 2026

Building and tuning this ruleset over July 2026 produced several transferable lessons for the JJC Cyberwolves training curriculum. See Cyberforce_2026/Cyberforce/Wazuh/ for training materials that reference this configuration as a case study.

Key takeaways:

  1. Shipped decoders often fail on containerized services. mailcow's double-wrapped log format broke both shipped Postfix and Dovecot decoders. Custom decoders were necessary.
  2. same_srcip alone is not enough. Modern brute force uses rotating IPs. Correlate on same_user, subnet, and volume too.
  3. Always test with real logs. Injected logs miss subtle formatting differences that break decoders.
  4. XML entities matter. < must be &lt;, > must be &gt;, both with trailing semicolons.
  5. PCRE2 vs OSSEC regex is not interchangeable. OSSEC lacks .*, ?, {n,m}, and negation classes. Use PCRE2 explicitly for complex patterns.

Last updated: 2026-07-31