Lesson 5 of 6·13 min read·Includes quiz

Wazuh Rules & Decoders

How Wazuh turns logs into alerts

What You'll Learn

  • Understand the full log processing pipeline: raw log → decoder → rule → alert
  • Read Wazuh decoder XML and explain how it extracts fields from raw logs
  • Read Wazuh rule XML and identify trigger conditions, severity, groups, and MITRE mappings
  • Explain rule chaining — how simple rules combine to detect complex attack patterns
  • Understand rule levels, frequency rules, and composite rules
  • Identify when a rule needs tuning and the principles of effective tuning

The Engine Behind the Alerts

In Lessons 2.1–2.4, you learned to read alerts, build dashboards, and search for events. But you've been treating the SIEM as a black box — logs go in, alerts come out. Now it's time to open that box and understand the engine.

Every alert you've investigated in this course was created by two components working together:

  1. Decoders — parse raw log text into structured fields
  2. Rules — evaluate those fields against detection logic and generate alerts

Understanding rules and decoders transforms you from a consumer of alerts to someone who can evaluate, tune, and eventually write detection logic. This is the bridge from L1 (triage) to L2 (investigation and detection engineering).

Why This Matters for Your Career: "Can you read a detection rule?" is a common SOC interview question. Understanding rule logic is expected for any analyst role above entry level. In Module 8 (Sigma), you'll write cross-platform detection rules — but first, you need to understand how rules work in one SIEM.

Log Processing Pipeline — From Raw Log to Alert


Stage 1: Decoders — Parsing Raw Logs

A raw log is just a line of text. Before any detection can happen, Wazuh needs to understand that text — extract the fields, identify the source, and structure the data for rule evaluation.

What a Raw Log Looks Like

Here's a raw SSH failure log from /var/log/auth.log:

Jan 15 06:20:18 linux-web-01 sshd[5102]: Failed password for root from 185.220.101.42 port 44921 ssh2

This is just a string. Wazuh needs to extract: the timestamp, hostname, program (sshd), the event type (failed password), the target user (root), the source IP (185.220.101.42), and the port (44921).

How Decoders Work

Decoders use regular expressions (regex) and field extraction patterns to parse raw logs. They work in a chain:

  1. Pre-decoder — extracts the standard syslog header (timestamp, hostname, program)
  2. Parent decoder — identifies the log source (e.g., "this is an sshd log")
  3. Child decoder — extracts event-specific fields (source IP, user, port)

Decoder XML Example

Here's a simplified version of Wazuh's SSH decoder:

<!-- Parent decoder: identifies sshd logs -->
<decoder name="sshd">
  <program_name>sshd</program_name>
</decoder>

<!-- Child decoder: extracts failed password fields -->
<decoder name="sshd-failed">
  <parent>sshd</parent>
  <prematch>^Failed password for </prematch>
  <regex>^Failed password for (\S+) from (\S+) port (\S+)</regex>
  <order>user, srcip, srcport</order>
</decoder>

Reading this decoder:

ElementPurposeValue
<parent>sshd</parent>Only applies to logs already identified as sshdChains to the parent decoder
<prematch>Quick check — does the log start with "Failed password for"?Fast filtering before regex
<regex>Captures three groups: username, IP, port\S+ matches any non-whitespace string
<order>Names the captured groupsuser → srcip → srcport

After decoding, the raw log becomes structured data:

FieldValue
program_namesshd
userroot
srcip185.220.101.42
srcport44921

4,000+ Built-in Decoders. Wazuh ships with decoders for hundreds of log formats: sshd, Apache, Windows Event Logs, iptables, named (DNS), sudo, PAM, auditd, and more. You rarely need to write decoders from scratch — but understanding how they work helps you troubleshoot when a rule doesn't fire because the decoder didn't extract the right field.


Stage 2: Rules — Detection Logic

Once the log is decoded into structured fields, the rule engine evaluates those fields against its detection rules. If a rule's conditions match, it generates an alert.

Rule XML Structure

Here's a simplified version of the SSH brute force rule you've encountered throughout this course:

<rule id="5551" level="10" frequency="10" timeframe="120">
  <if_matched_sid>5503</if_matched_sid>
  <same_source_ip />
  <description>sshd: brute force trying to get access to the system.</description>
  <mitre>
    <id>T1110</id>
  </mitre>
  <group>authentication_failed,sshd,</group>
</rule>

Reading this rule field by field:

ElementPurposeValue
id="5551"Unique identifier — this is the rule.id you see in alerts5551
level="10"Severity on the 0-15 scale — High10
frequency="10"Must see this condition 10 times before firingThreshold-based detection
timeframe="120"Within a 120-second (2-minute) windowTime constraint
<if_matched_sid>5503</if_matched_sid>Depends on rule 5503 (single SSH failure) having firedRule chaining
<same_source_ip />All 10 occurrences must come from the same IPCorrelation constraint
<description>Human-readable explanationWhat analysts see in the dashboard
<mitre><id>T1110</id></mitre>Maps to ATT&CK techniqueBrute Force
<group>Category tagsUsed for filtering and searching

What This Rule Actually Says

In plain English: "If rule 5503 (single SSH failure) fires 10 times from the same source IP within a 2-minute window, generate a level 10 alert for SSH brute force (T1110)."

This is a frequency rule — it doesn't fire on a single event. It requires a pattern: multiple occurrences of a simpler rule within a time window. This is how Wazuh reduces noise — one failed login is level 5 (low). Ten failed logins from the same IP in 2 minutes is level 10 (high).

Rule XML Anatomy — Understanding Detection Logic


Rule Types

Wazuh has several rule types, each designed for different detection patterns:

Atomic Rules (Single Event)

The simplest type — matches a single event based on decoded fields.

<rule id="5503" level="5">
  <if_sid>5500</if_sid>
  <match>Failed password</match>
  <description>sshd: authentication failed.</description>
</rule>

This fires once for every single failed SSH password attempt. Level 5 (low) because one failure is normal — people mistype passwords.

Frequency Rules (Threshold)

Fires when an atomic rule triggers N times within a time window — like rule 5551 above.

ElementPurpose
frequencyHow many times the referenced rule must fire
timeframeThe time window (in seconds) for counting occurrences
<same_source_ip />Group by source IP (count per IP, not total)
<same_user />Group by username
<if_matched_sid>Which rule to count

Composite Rules (Multi-Stage)

The most advanced type — chains multiple different rules in sequence to detect multi-step attack patterns.

<rule id="100001" level="12">
  <if_sid>5551</if_sid>
  <if_fts>srcip</if_fts>
  <description>Brute force attack followed by successful login from same IP</description>
</rule>

This would fire when a brute force alert (5551) is followed by a successful login from the same IP — indicating the brute force may have succeeded.

Rule Chaining Is How Wazuh Detects Attacks, Not Just Events. An individual failed login is an event. Ten failed logins is a pattern. Ten failed logins followed by a success is an attack. Rule chaining enables this escalation from event → pattern → attack by building rules that reference other rules.


Rule Levels: The Severity Assignment

Rule levels aren't arbitrary — they follow a hierarchy that reflects the security significance of the detected event:

Level RangeMeaningRule Design Intent
0IgnoredRule exists but doesn't generate alerts (used for filtering)
1-3InformationalNormal system activity — logged but no action needed
4-6LowSingle suspicious events — worth logging, not worth investigating individually
7-9MediumAnomalous activity — investigate within the hour
10-12HighLikely malicious activity — investigate immediately
13-15CriticalActive attack or confirmed compromise — all-hands response

How Levels Interact with Frequency

Notice the escalation pattern:

ScenarioRuleLevel
1 failed SSH login55035 (Low)
10 failed SSH logins from same IP in 2 min555110 (High)
Brute force followed by successCustom12 (High)
Same pattern + privilege escalationCustom14 (Critical)

Each step up the chain increases severity because the accumulated evidence is stronger. This is the fundamental principle of detection engineering: context elevates confidence.


Rule Groups and MITRE Mapping

Groups

Groups are tags that categorize rules for filtering and searching:

<group>authentication_failed,sshd,pci_dss_10.2.4,nist_800_53_AU.14,</group>
GroupPurpose
authentication_failedFunctional category — what type of event
sshdSource — which service generated it
pci_dss_10.2.4Compliance mapping — PCI DSS requirement
nist_800_53_AU.14Compliance mapping — NIST 800-53 control

Groups let you search broadly. Instead of memorizing dozens of individual rule IDs, you can search rule.groups:authentication_failed to find all failed auth events regardless of source (SSH, Windows, PAM, etc.).

MITRE ATT&CK Mapping

Modern Wazuh rules include ATT&CK technique IDs:

<mitre>
  <id>T1110</id>
</mitre>

This maps the rule to ATT&CK T1110 (Brute Force). In the dashboard, you'll see this in the rule.mitre field with the tactic (Credential Access) and technique name. This is what connects your SIEM alerts to the ATT&CK framework you mapped in Lab 1.2.


Rule Tuning: Reducing Noise Without Losing Signal

The most practical skill related to rules is tuning — adjusting rules to reduce false positives while preserving detection of real threats. Poor tuning is the #1 cause of alert fatigue in SOCs.

Why Rules Need Tuning

Out-of-the-box rules are designed to be broadly effective across many environments. But every environment is different:

ProblemExampleImpact
Too sensitiveFIM alerts every time a log file rotatesHundreds of daily FP alerts for /var/log files
Wrong contextBrute force rule fires when IT runs password auditsHigh-severity alerts for authorized activity
Missing exclusionsService accounts trigger "new logon" alerts every 4 hoursPredictable noise that buries real alerts
Threshold too lowRule fires on 3 failures — normal for users mistypingAlert fatigue from common mistakes

Tuning Strategies

StrategyHowWhen
Adjust thresholdIncrease frequency from 10 to 20FP rate is high but the rule catches real attacks at higher counts
Narrow scopeAdd <srcip>!10.0.1.0/24</srcip> to exclude internal IPsInternal scanners trigger the rule
Exclude known-goodAdd exception for specific service accountsAutomated processes trigger human-focused rules
Lower severityChange level from 10 to 7The event is worth logging but doesn't need immediate investigation
Time-based exceptionSuppress during maintenance windowsPlanned changes trigger FIM and new service alerts
🚨

Never Disable a Rule — Tune It. Disabling a rule removes detection entirely. Instead, adjust the threshold, add exclusions, or lower the severity. The goal is to keep the rule active for real threats while suppressing known-good activity. A disabled rule can't catch the one real attack that looks like the false positives you were ignoring.

The Tuning Workflow

  1. Identify the noisy rule from your dashboard's top rules widget (Lesson 2.3)
  2. Analyze the false positives — what's triggering them? Same IP? Same user? Same schedule?
  3. Design an exclusion that covers the FP pattern without blocking real attacks
  4. Test the tuned rule against historical data — does it still catch the known-good alerts?
  5. Deploy and monitor — track FP rate for the rule over the next week
  6. Document the change — what you tuned, why, and what to watch for

Rule Tuning Workflow — Reduce Noise, Keep Signal


Where Rules Live

Wazuh rules are stored in XML files organized by category:

PathContents
/var/ossec/ruleset/rules/Built-in rules (4,000+) — never edit these directly
/var/ossec/etc/rules/local_rules.xmlCustom rules and overrides — your changes go here
/var/ossec/ruleset/decoders/Built-in decoders — never edit these directly
/var/ossec/etc/decoders/local_decoder.xmlCustom decoders — your changes go here

The separation between built-in and local files is critical: when Wazuh updates, the built-in rules are replaced. Any changes you made to built-in files would be lost. Always put customizations in the local_ files.

💡

Reading Rule Files Is a Superpower. When an alert fires and you want to understand exactly why, find the rule file (/var/ossec/ruleset/rules/0095-sshd_rules.xml for SSH rules), search for the rule ID, and read the conditions. This is faster and more accurate than Googling the rule ID — and it works offline.


Putting It Together: Following a Log Through the Pipeline

Let's trace a single event from raw log to alert to reinforce the complete pipeline:

1. Raw log arrives:

Jan 15 06:25:01 linux-web-01 sshd[5150]: Failed password for root from 185.220.101.42 port 45030 ssh2

2. Pre-decoder extracts syslog header:

  • Timestamp: Jan 15 06:25:01
  • Hostname: linux-web-01
  • Program: sshd

3. Parent decoder (sshd) matches based on program_name.

4. Child decoder (sshd-failed) extracts fields:

  • user: root
  • srcip: 185.220.101.42
  • srcport: 45030

5. Rule 5503 matches ("Failed password" string matched) → generates a level 5 internal event.

6. Rule 5551 checks frequency: Has rule 5503 fired 10+ times from the same source IP in the last 120 seconds?

  • If yes → Alert generated: Level 10, rule 5551, "sshd: brute force trying to get access to the system."
  • If no → Event logged but no brute force alert yet.

7. Alert appears in the dashboard with all decoded fields, the rule description, severity level, MITRE mapping, and the original raw log preserved in full_log.


Key Takeaways

  • Decoders parse raw log text into structured fields using regex and extraction patterns — they work in chains (pre-decoder → parent → child)
  • Rules evaluate decoded fields against detection logic — atomic rules match single events, frequency rules require thresholds, composite rules chain multiple detections
  • Rule 5551 (SSH brute force) requires rule 5503 (single failure) to fire 10 times from the same IP within 120 seconds — this escalation pattern reduces false positives
  • Rule levels follow a hierarchy: 0-3 informational, 4-6 low, 7-9 medium, 10-12 high, 13-15 critical
  • Rule groups enable broad category searches; MITRE mappings connect rules to the ATT&CK framework
  • Tuning rules (adjusting thresholds, adding exclusions, lowering severity) is essential to reduce alert fatigue — never disable a rule entirely
  • Custom rules go in local_rules.xml, custom decoders in local_decoder.xml — never edit built-in files

Knowledge Check: Wazuh Rules & Decoders

10 questions · 70% to pass

1

What is the purpose of a Wazuh decoder in the log processing pipeline?

2

In rule 5551 (SSH brute force), the XML contains frequency='10' and timeframe='120'. What does this mean in plain English?

3

A rule fires on every log rotation event for /var/log/*.log files, generating 200 false positive FIM alerts daily. What is the best tuning approach?

4

Why should custom rules always go in local_rules.xml instead of modifying the built-in rule files?

5

An analyst sees a level 5 alert (single SSH failure, rule 5503) and a level 10 alert (brute force, rule 5551) from the same IP. What detection engineering principle explains the severity difference?

6

In a Wazuh decoder, what is the purpose of the <prematch> element?

7

What type of Wazuh rule would detect: 'SSH brute force from an IP, followed by a successful login from the same IP within 5 minutes'?

8

In Lab 1.1, you saw rule 5551 fire for the SSH brute force attack. Based on what you now know about rule XML, what is the parent rule that 5551 depends on (via <if_matched_sid>)?

9

In Lab 1.3, you encountered rule 5902 (new Windows service installed) on WIN-SERVER-01. If this rule fires 50 times daily because IT deploys services frequently, what is the best tuning approach that preserves security detection?

10

In the lab data, rule 80790 (Windows event log cleared) fires at level 15. Based on the rule level hierarchy in this lesson, why is log clearing classified as critical severity?

0/10 answered