Learn Wazuh - Custom Rules & Advanced Detection
Series/Learn Wazuh/Episode 17
Episode 17 of 23

Learn Wazuh - Custom Rules & Advanced Detection

Creating custom rules for specific use cases like brute force, reverse shell, and data exfiltration, using variables and dynamic fields, then diving into advanced detection: cross-agent correlation, alert overrides, GeoIP enrichment, and threat intel feed integration.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 16, you made sure agent and manager communication runs securely via mTLS and X.509 certificates. A secure channel is only valuable if something intelligent reads what passes through it. That's where this episode builds that intelligence.

Wazuh's built-in rules are excellent for common patterns, but every environment has its uniqueness. Internal applications, custom log formats, and specific attack scenarios aren't always caught by generic rules. Episode 17 teaches how to write custom rules for your needs.

We'll create rules for brute force, reverse shell, and data exfiltration, use variables and dynamic fields, then dive into advanced detection: cross-agent correlation, alert overrides, GeoIP enrichment, and threat intel feed integration. This is the favorite episode of detection engineers.

Why Built-in Rules Aren't Enough

Imagine an internal web application whose failed-login log doesn't use the standard SSH or Apache format. Built-in rules won't recognize it because the format is unfamiliar. That's where custom rules work: they capture signals from logs only your environment understands.

There are also cases where an attack pattern is so specific it names particular files or commands that only appear in certain environments. Built-in rules can't possibly guess everything, so you write them yourself.

The principle is simple: first understand the logs you have, then write rules that capture suspicious behavior in them. Don't write rules without looking at real log samples, because the result will be a fragile guess.

Wazuh Rule Anatomy

Wazuh rules are written in XML and stored in the /var/ossec/etc/rules/ directory. Custom rules should be placed in local_rules.xml so they're easy to distinguish from built-in rules and won't be overwritten during upgrades.

A <rule> block always has an id and level. The level determines how important an alert is, from 0 which isn't considered, up to 16 which is critical. Beyond that, a rule can use elements like match to match text, if_sid to depend on another rule, and frequency to count occurrences within a time window.

After writing a rule, validation before use is mandatory. Run wazuh-analysisd -t to check the syntax, then restart the service to activate the rule.

Creating Your Own Brute Force Rule

Brute force is one of the most common use cases handled by custom rules. Its pattern is typical: many failed logins from the same source in a short time, then one success at the end.

Wazuh already has rules for SSH login failures. We just build on top of them with a rule that counts occurrences and requires the same source IP. The combination of frequency and timeframe is the heart of this detection.

LinuxDetect SSH brute force within 2 minutes
<group name="local,ssh_bruteforce,">
  <rule id="100300" level="10" frequency="8" timeframe="120">
    <if_matched_sid>5710</if_matched_sid>
    <same_srcip />
    <description>Delapan kegagalan login dari satu sumber</description>
    <mitre>
      <id>T1110</id>
    </mitre>
  </rule>
</group>

The rule above waits for eight events triggering rule 5710 within 120 seconds from the same source IP, then reports it as a brute force attack. The MITRE ATT&CK label is also included so the alert is rich in context.

Variables and Dynamic Fields

A good rule doesn't just attach text; it also extracts values from events for use elsewhere. Wazuh calls these dynamic fields, and their values can be inserted into alert descriptions.

Dynamic fields are written with a $ sign followed by the field name, like $(srcip), $(user), or $(url). Their values come from the event decoding results, so one rule can produce a different description for each occurrence.

LinuxDynamic fields in a rule description
<rule id="100301" level="5">
  <match>login attempted</match>
  <description>Login gagal untuk user $(user) dari IP $(srcip)</description>
  <group>authentication_failures,</group>
</rule>

When a matching event comes in, the alert description automatically fills in the relevant user name and source IP. This lets analysts read alerts directly without opening the original log.

Reverse Shell and Data Exfiltration Rules

A reverse shell is the most dangerous moment in an attack: the attacker already has command execution on the host. Early detection is crucial, and command auditing is the primary source.

With the auditd module active, every command a user runs is recorded as an event. Rules can catch command patterns identical to reverse shells, such as invoking an interactive shell without a terminal.

LinuxReverse shell indication on the command line
<group name="local,reverse_shell,">
  <rule id="100302" level="14">
    <if_sid>80700</if_sid>
    <match>bash -i</match>
    <description>Kemungkinan reverse shell terdeteksi</description>
    <mitre>
      <id>T1059</id>
    </mitre>
  </rule>
</group>

Data exfiltration follows a similar pattern. Watch for commands that move data outward, combinations of archive wrapping with network transfer, or sending data to unknown addresses. The key is building a normal command profile first, then flagging what deviates.

Alert Overrides

Sometimes built-in rules pick a level that doesn't fit your policy. Some are so noisy that analysts go deaf, or too low for events you consider important. Alert overrides solve this.

The way it works is simple: rewrite the rule with the same id and the overwrite="yes" attribute, then give new values for the attributes you want to change. Attributes you don't write keep their original values.

LinuxLowering the level of a noisy rule
<rule id="2407" level="5" overwrite="yes">
  <description>Kegiatan syslog rutin yang dipantau</description>
  <group>overridden_syslog_group,</group>
</rule>

With an override, you don't need to delete built-in rules or disable an entire file. One small block in local_rules.xml is enough to adjust behavior to your team's needs.

Multi-Agent Correlation

So far we've talked about correlation within a single host. But attacks often spread: one source tries to break into many servers at once. By default, the frequency counter only counts events from the same agent, so cross-host patterns escape detection.

Wazuh provides the global_frequency option to count events from all agents at once. Combine it with same_srcip, and you can detect distributed brute force attempts that wouldn't be visible if each agent stood alone.

LinuxCross-agent correlation with global_frequency
<group name="local,distributed_attack,">
  <rule id="100303" level="12" frequency="20" timeframe="300">
    <if_matched_sid>5710</if_matched_sid>
    <global_frequency />
    <same_srcip />
    <description>Brute force tersebar ke banyak agent dari satu sumber</description>
  </rule>
</group>

Remember, global_frequency works at the manager level, not cluster. On very large deployments, consider load sharing so correlation stays light.

GeoIP Enrichment

A source IP that's just a number isn't very informative. GeoIP enrichment adds country, city, and coordinates to alerts, so analysts immediately see where a connection comes from without searching external databases.

Wazuh uses a GeoIP database downloaded and stored on the manager side. The geoip module is enabled via ossec.conf, then mapping rules determine which alerts get location data.

LinuxEnabling GeoIP enrichment
<ossec_config>
  <geoip enabled="yes" mode="fast" count="10" data_path="/etc/geolite2">
    <ruleset>
      <rule file="/var/ossec/etc/ossec-geoip.conf">
        <rules>
          <rule id="100300">geoip</rule>
          <rule id="100303">geoip</rule>
        </rules>
      </rule>
    </ruleset>
  </geoip>
</ossec_config>

Remember to update the GeoIP database regularly, because country boundaries and IP block allocations change over time. A stale database makes location conclusions misleading.

Threat Intel Feed Integration

GeoIP answers the question of where from, while threat intel answers whether a source is known to be dangerous. Wazuh can be connected to various threat feeds to enrich every alert.

The most popular integration is VirusTotal, which checks file hashes against a global antivirus collection. Its configuration is fairly simple and produces additional fields on syscheck alerts.

LinuxVirusTotal integration as threat intel
<ossec_config>
  <integration>
    <name>virustotal</name>
    <api_key>API_KEY_ANDA</api_key>
    <group>syscheck</group>
    <alert_format>json</alert_format>
  </integration>
</ossec_config>

Besides VirusTotal, you can use an IP reputation list file via <list>, or route alerts to another threat intel service with a custom integration. Choose feeds relevant to the threats you face, because every API request also carries cost.

Conclusion

Episode 17 taught you to write custom rules and bring advanced detection to life. We created rules for brute force, reverse shell, and data exfiltration, used variables and dynamic fields, then explored alert overrides, cross-agent correlation, GeoIP enrichment, and threat intel feed integration.

Key takeaways:

  • Custom rules are written in local_rules.xml and must be validated with wazuh-analysisd.
  • The combination of frequency and timeframe catches brute force patterns.
  • Dynamic fields keep alert descriptions always relevant to the event.
  • Overrides adjust rule levels without deleting built-in rules.
  • global_frequency counts events from all agents at once.
  • GeoIP and threat intel enrich alerts with external context.

Your detection is now very sharp, but detection without response only produces a list of alerts. In episode 18, we'll connect Wazuh to TheHive for case management and Shuffle for orchestration, building a complete incident response flow. See you there!

Learn Wazuh - Custom Rules & Advanced Detection | Learn Wazuh