Before writing more rules, it's worth understanding what already exists. Wazuh ships with hundreds of built-in rules. Writing rules that duplicate existing coverage wastes time. The goal of this session: map default coverage, identify the gaps, and prioritise what to build next.
Finding default MITRE coverage
sudo grep -rh "<id>T" /var/ossec/ruleset/rules/ | sed 's/.*<id>//;s/<\/id>.*//' | sort -u
This returns every MITRE technique ID with at least one built-in rule. That's the baseline. Everything not on that list is a potential gap.
Installing Atomic Red Team
Atomic Red Team is a library of safe attack simulations — small scripts that replicate specific MITRE technique behaviour patterns without causing damage. Used to confirm that a technique generates detectable events before writing rules for it.
Set-ExecutionPolicy Bypass -Scope CurrentUser -Force
IEX (IWR 'https://raw.githubusercontent.com/redcanaryco/invoke-atomicredteam/master/install-atomicredteam.ps1' -UseBasicParsing)
Install-AtomicRedTeam -getAtomics -Force
Priority tactics for a Windows endpoint environment
- Credential Access — most attacks need credentials
- Defense Evasion — attackers hide their tracks
- Execution — how malicious code runs
- Persistence — how attackers maintain access
- Lateral Movement — how they spread
- Discovery — reconnaissance activity
- Exfiltration — data leaving the environment
T1110.003 — Password spraying: a confirmed gap
sudo grep -r "T1110.003\|spray" /var/ossec/ruleset/rules/ -i
Empty result — confirmed gap. Password spraying is distinct from brute force. Brute force tries many passwords against one account. Spraying tries one password against many accounts. The detection logic is different:
- Brute force:
same_field— same username, many failures - Spray:
different_srcuser— different usernames, same source IP
Writing the password spray detection
<rule id="100011" level="5">
<if_sid>100002</if_sid>
<description>Windows: Failed logon — password spray candidate</description>
<group>windows_failed_logon,spray_candidate,</group>
<mitre><id>T1110.003</id></mitre>
</rule>
<rule id="100012" level="12" frequency="5" timeframe="60">
<if_matched_sid>100011</if_matched_sid>
<different_srcuser />
<same_field>win.eventdata.ipAddress</same_field>
<description>Windows: Password spraying detected</description>
<group>windows_password_spray,</group>
<mitre><id>T1110.003</id></mitre>
</rule>
Five failed logins against different usernames from the same IP within 60 seconds triggers the alert. Tested with five runas commands against different non-existent accounts — fired at level 12.
Group tags as multi-condition accumulators
A pattern that emerged from building these rules: individual detection rules each tag a shared group. A correlation rule listens to that group and fires when enough events accumulate. This enables multi-condition alerting where the final alert only fires when multiple independent indicators are present — not on any single event alone.