Wazuh ships with thousands of rules, most of which are noise. The value comes from understanding the rule engine well enough to write detections that actually matter for your environment.

Rule Anatomy

Every Wazuh rule follows the same structure:

<rule id="100001" level="10">
<if_sid>92200</if_sid>
<field name="win.eventdata.commandLine" type="pcre2">(?i)(mimikatz|sekurlsa|logonpasswords)</field>
<description>Mimikatz keywords detected in process command line</description>
<mitre>
<id>T1003.001</id>
</mitre>
<group>credential_access,windows,</group>
</rule>

Key fields:

  • id — unique, start custom rules at 100000+
  • level — 0-15, drives alerting thresholds
  • if_sid — parent rule that must match first (decoder chain)
  • field — log field to match against
  • type="pcre2" — use Perl-compatible regex

Decoder Chaining

The most powerful pattern is chaining decoders to build context before the rule fires. This prevents false positives on incomplete data.

<!-- Parent: only fire when we have a Windows Security event -->
<rule id="100010" level="0">
<if_sid>60103</if_sid>
<field name="win.system.eventID">^4688$</field>
<description>New process created</description>
</rule>
<!-- Child: apply additional logic only on 4688 events -->
<rule id="100011" level="12">
<if_sid>100010</if_sid>
<field name="win.eventdata.newProcessName" type="pcre2">(?i)\\(powershell|cmd|wscript|cscript)\.exe$</field>
<field name="win.eventdata.parentProcessName" type="pcre2">(?i)\\(winword|excel|outlook)\.exe$</field>
<description>Office application spawning shell interpreter</description>
<mitre>
<id>T1566.001</id>
</mitre>
</rule>

Frequency-Based Rules

Brute force, password spray, and reconnaisance patterns require frequency matching:

<rule id="100020" level="10" frequency="10" timeframe="60">
<if_matched_sid>60204</if_matched_sid>
<same_field>win.eventdata.targetUserName</same_field>
<description>10+ failed logons for same user within 60 seconds — possible brute force</description>
<mitre>
<id>T1110.001</id>
</mitre>
</rule>

The frequency + timeframe + same_field combination is the standard pattern for rate-based detections.

Testing Rules

Before deploying, test rules against sample logs using wazuh-logtest:

Terminal window
/var/ossec/bin/wazuh-logtest

Paste a raw log line and Wazuh will show you which decoders and rules fire — invaluable for debugging.

The rule engine is deterministic. If your detection isn’t firing, the issue is almost always in the decoder chain or field naming, not the regex itself.