Files
2026-07-31 16:43:53 +00:00

152 lines
6.2 KiB
Markdown

# 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:
```bash
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`:
```bash
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:**
```bash
# 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:**
```bash
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