How to Set Wazuh Custom Rules in Wazuh Manager(Ubuntu)

Custom rules in Wazuh allow you to define specific conditions or patterns in log data relevant to your unique environment and security requirements. This guide covers the essential steps to create, modify, and manage custom rules in the Wazuh manager.

Understanding Custom Rules

Wazuh enables you to add custom rules and modify existing default rules . Custom rules should use ID numbers between 100000 and 120000 to avoid conflicts with out-of-the-box system rules .

Rule File Locations

For minor adjustments, use the /var/ossec/etc/rules/local_rules.xml file. For changes on a larger scale, create new rule files in the /var/ossec/etc/rules/ directory .

Creating a New Custom Rule

Step 1: Access the Rules Directory

Navigate to the Wazuh server and open the rules directory:

cd /var/ossec/etc/rules/

Step 2: Create or Edit the Custom Rules File

Edit the local_rules.xml file:

vi /var/ossec/etc/rules/local_rules.xml

Step 3: Add Your Custom Rule

Basic rule structure example:

<group name="custom_rules_example,">
  <rule id="100010" level="0">
    <program_name>example</program_name>
    <description>User logged</description>
  </rule>
</group>

Step 4: Test the Rule

Use the wazuh-logtest utility to test your rule without restarting the manager:

/var/ossec/bin/wazuh-logtest

Input your log sample to verify the rule fires correctly .

Step 5: Apply the Changes

Restart the Wazuh manager to generate alerts based on the changes:

systemctl restart wazuh-manager
# or
service wazuh-manager restart

Essential Rule Syntax Elements

Basic Rule Components

ElementDescriptionExample
<rule>Defines the rule with ID and level attributes<rule id="100010" level="5">
<if_sid>Makes this rule a child of another rule ID<if_sid>5700</if_sid>
<match>Searches for a pattern in the log using sregex<match>illegal user</match>
<regex>Searches using PCRE2 regular expressions<regex>user '(\w+)'</regex>
<description>Human-readable explanation of the rule<description>Failed login attempt</description>
<group>Categorizes the alert<group>authentication_failed,</group>

Field Comparison Options

ElementPurpose
<field name="field_name">Compare a decoded field with a regex
<srcip>Compare with source IP address
<dstip>Compare with destination IP address
<program_name>Match against pre-decoded program name
<hostname>Match against hostname

Rule Options and Attributes

AttributeDescriptionValues
levelSeverity level (0-16)0-16
frequencyTimes rule must match before firing2-9999
timeframeTime period in seconds for frequency1-99999
ignoreSeconds to ignore after firing1-999999
noalertDon’t generate alert0 or 1
overwriteOverride existing ruleyes, no

Modifying Existing Rules

To preserve changes during upgrades, never edit files in /var/ossec/ruleset/rules/ directly .

Step 1: Copy the Default Rule

Find the rule in /var/ossec/ruleset/rules/ and copy its definition.

Step 2: Paste and Modify in local_rules.xml

Add the rule to /var/ossec/etc/rules/local_rules.xml with the overwrite="yes" attribute:

<group name="syslog,sshd,">
  <rule id="5710" level="10" overwrite="yes">
    <if_sid>5700</if_sid>
    <match>illegal user|invalid user</match>
    <description>sshd: Attempt to login using a non-existent user</description>
  </rule>
</group>

Important: The tags if_sid, if_group, if_level, if_matched_sid, and if_matched_group cannot be overwritten and preserve original values .

Advanced Rule Examples

Example 1: Detecting Specific File Extensions

Trigger when a file with .encrypted extension is added:

<group name="syscheck,">
  <rule id="100021" level="5">
    <if_sid>554</if_sid>
    <field name="file">.encrypted$</field>
    <description>$(file) has been encrypted</description>
  </rule>
</group>

Example 2: Negated Pattern Matching

Trigger when a file with non-allowed extension is added:

<group name="syscheck,">
  <rule id="100022" level="5">
    <if_sid>554</if_sid>
    <field name="file" negate="yes">.txt$|.png$|.jpg$|.dat$</field>
    <description>$(file) with disallowed extension added</description>
  </rule>
</group>

Example 3: Frequency-Based Detection

Trigger after 8 matches in 120 seconds:

<rule id="100030" level="10" frequency="8" timeframe="120">
  <if_matched_sid>5710</if_matched_sid>
  <same_source_ip />
  <description>Multiple failed SSH logins from same source</description>
</rule>

Example 4: Time-Based Rules

Trigger only during specific hours:

<rule id="100040" level="7">
  <if_sid>5710</if_sid>
  <time>22:00-06:00</time>
  <weekday>weekdays</weekday>
  <description>SSH failure outside business hours</description>
</rule>

Testing Rules with wazuh-logtest

The wazuh-logtest tool validates rules without restarting the manager :

/var/ossec/bin/wazuh-logtest

Example output:

Type one log per line

Dec 25 20:45:02 MyHost example[12345]: User 'admin' logged from '192.168.1.100'

**Phase 3: Completed filtering (rules).
        id: '100010'
        level: '0'
        description: 'User logged'
        groups: '['custom_rules_example']'
        firedtimes: '1'
        mail: 'False'

Ruleset as Code (RaC) Approach

For enterprise environments, implement Ruleset as Code using version control :

Repository Structure

Track these directories in Git:

  • /var/ossec/etc/decoders/
  • /var/ossec/etc/rules/

CI/CD Pipeline Integration

Example GitHub Actions workflow for automatic deployment:

name: Update Rulesets on SIEM
on:
  push:
    branches: [ "main" ]
    paths: ["**.xml"]

jobs:
  DaaC:
    runs-on: ubuntu-latest
    steps:
      - name: Apply rulesets to SIEM
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            sudo bash -c '
            cd /var/ossec/etc/
            git pull origin main
            chown wazuh:wazuh /var/ossec/etc/rules/* && chmod 660 /var/ossec/etc/rules/*
            sudo systemctl restart wazuh-manager
            '

Rule ID Conflict Prevention

When implementing RaC, include rule ID conflict checking :

  • Scan for duplicate IDs between branches
  • Validate rule IDs are within custom range (100000-120000)
  • Check for proper XML syntax

Common Rule Patterns

PCRE2 Regex Tips

PatternMeaningExample
\d+One or more digitsport \d+ matches “port 3389”
\w+One or more word charsuser: \w+ matches “user: admin”
^Start of line^sshd matches lines starting with “sshd”
$End of lineerror$ matches lines ending with “error”
|OR operatorerror|fail matches either word

Field Extraction

Fields extracted by decoders can be referenced:

  • $(field_name) – Use extracted value in description
  • <field name="field_name">pattern</field> – Match against extracted field

Troubleshooting

Rule Not Firing

  1. Test with wazuh-logtest first
  2. Check XML syntax validation
  3. Verify rule ID not conflicting
  4. Ensure proper rule hierarchy (if_sid references)
  5. Check decoder is extracting fields correctly

Common Issues

  • Rule ID conflicts: Stay within 100000-120000 range
  • Overwrite not working: Remember certain tags cannot be overwritten
  • Changes not applying: Always restart manager after finalizing
  • False positives: Use more specific matches or add negations

Summary Checklist

  • [ ] Use rule IDs 100000-120000 for custom rules
  • [ ] Place rules in /var/ossec/etc/rules/ directory
  • [ ] Test with wazuh-logtest before applying
  • [ ] Use overwrite="yes" when modifying existing rules
  • [ ] Never edit default rules directly (always copy first)
  • [ ] Restart manager to apply changes
  • [ ] For enterprise: implement version control and CI/CD
  • [ ] Document rule purpose and expected behavior

By following these guidelines, you can effectively extend Wazuh’s detection capabilities to match your specific security monitoring requirements while maintaining upgrade compatibility.

Distribution-Specific Differences

RHEL/CentOS/Rocky Linux/AlmaLinux

# Service management
systemctl restart wazuh-manager
# or
service wazuh-manager restart

# Installation paths (same as Ubuntu)
/var/ossec/  # Main installation directory
/var/ossec/etc/rules/local_rules.xml
/var/ossec/bin/wazuh-logtest

Amazon Linux

# Same as RHEL
systemctl restart wazuh-manager

# Paths identical
/var/ossec/etc/rules/local_rules.xml

OpenSUSE

# Service management
systemctl restart wazuh-manager

# Same paths
/var/ossec/etc/rules/local_rules.xml

Arch Linux

# Service management
systemctl restart wazuh-manager

# Same paths
/var/ossec/etc/rules/local_rules.xml

FreeBSD

# Service management (if installed from ports/pkg)
service wazuh-manager restart
# or
/usr/local/etc/rc.d/wazuh-manager restart

# Paths (may vary)
/usr/local/ossec-hids/etc/rules/local_rules.xml
/usr/local/ossec-hids/bin/ossec-logtest  # Note: wazuh-logtest might be ossec-logtest

Windows (Wazuh agent manager)

# Service management (PowerShell as Administrator)
Restart-Service -Name "WazuhManager"

# Paths
C:\Program Files (x86)\ossec-agent\  # Note: Different path structure
# Rules location may vary based on installation

macOS

# Service management
launchctl unload /Library/LaunchDaemons/com.wazuh.agent.plist
launchctl load /Library/LaunchDaemons/com.wazuh.agent.plist

# Paths
/Library/Ossec/etc/rules/local_rules.xml
/Library/Ossec/bin/ossec-logtest

Key Universal Concepts

Despite distribution differences, these remain consistent:

  1. Rule ID range: Always use 100000-120000 for custom rules
  2. local_rules.xml: Primary file for custom rules (path may vary slightly)
  3. wazuh-logtest: Testing tool (may be named ossec-logtest on older/other systems)
  4. Restart required: Changes require manager restart
  5. XML syntax: Same across all platforms
  6. Rule structure: Identical regardless of OS

Docker/Kubernetes Deployments

For containerized Wazuh:

# Docker exec into container
docker exec -it wazuh-manager bash

# Then standard paths inside container
/var/ossec/etc/rules/local_rules.xml

# Restart container (not just service)
docker restart wazuh-manager

Quick Distribution Check

To verify your distribution:

# Check OS
cat /etc/os-release
# or
uname -a

# Check Wazuh installation path
find / -name "ossec.conf" 2>/dev/null
# or
find / -name "wazuh-logtest" 2>/dev/null

Wazuh Custom Rule Examples

Here are practical, ready-to-use custom rule examples for common scenarios:

Authentication & Access Rules

1. Detect Multiple Failed SSH Logins (Brute Force)

<group name="ssh,authentication_failures,">
  <!-- Individual failed login -->
  <rule id="100001" level="5">
    <if_sid>5700</if_sid>
    <match>^Failed password|^Failed publickey|^error: PAM: Authentication failure</match>
    <description>SSH: Failed login attempt</description>
    <group>authentication_failed,</group>
  </rule>

  <!-- 5+ failures in 60 seconds from same IP -->
  <rule id="100002" level="10" frequency="5" timeframe="60">
    <if_matched_sid>100001</if_matched_sid>
    <same_source_ip />
    <description>SSH: Possible brute force attack - multiple failures from same IP</description>
    <group>authentication_failed,brute_force,</group>
  </rule>
</group>

2. Root Login Detection

<group name="authentication,privilege_escalation,">
  <rule id="100010" level="8">
    <if_sid>5500</if_sid>
    <match>session opened for user root</match>
    <description>Root session opened</description>
    <group>root_access,pci_dss_10.2.2,</group>
  </rule>

  <rule id="100011" level="10">
    <if_sid>5500</if_sid>
    <match>FAILED LOGIN|authentication failure</match>
    <regex type="pcre2">(root|toor).*(FAILED LOGIN|authentication failure)</regex>
    <description>Failed root login attempt</description>
    <group>authentication_failed,root_access,</group>
  </rule>
</group>

3. Sudo Command Usage Tracking

<group name="sudo,privilege_escalation,">
  <rule id="100020" level="3">
    <program_name>sudo</program_name>
    <match>COMMAND=</match>
    <description>Sudo command executed by $(user): $(sudo_command)</description>
    <group>command_execution,</group>
  </rule>

  <rule id="100021" level="7">
    <if_sid>100020</if_sid>
    <regex type="pcre2">COMMAND=(/bin/su|/usr/bin/su|/bin/bash -i|/bin/sh -i)</regex>
    <description>Sensitive sudo command: $(sudo_command) executed by $(user)</description>
    <group>command_execution,privilege_escalation,</group>
  </rule>
</group>

File Integrity Monitoring (FIM)

4. Critical File Changes Alert

<group name="syscheck,fim,">
  <!-- Detect changes to /etc/passwd -->
  <rule id="100030" level="7">
    <if_sid>550</if_sid>
    <field name="file">/etc/passwd</field>
    <description>Critical file modified: /etc/passwd</description>
    <group>syscheck,pci_dss_11.5,</group>
  </rule>

  <!-- Detect changes to SSH configuration -->
  <rule id="100031" level="8">
    <if_sid>550</if_sid>
    <field name="file">/etc/ssh/sshd_config</field>
    <description>SSH configuration changed: /etc/ssh/sshd_config</description>
    <group>syscheck,configuration_change,</group>
  </rule>

  <!-- Detect suspicious file extensions (ransomware pattern) -->
  <rule id="100032" level="12">
    <if_sid>554</if_sid>
    <field name="file">\.(encrypted|locked|crypt|encrypt)$</field>
    <description>POSSIBLE RANSOMWARE: File with suspicious extension added: $(file)</description>
    <group>syscheck,ransomware,</group>
  </rule>
</group>

5. Web Directory Monitoring

<group name="syscheck,web,">
  <!-- New files in web directories -->
  <rule id="100040" level="7">
    <if_sid>554</if_sid>
    <field name="file">/var/www/.*\.(php|asp|aspx|jsp|pl|cgi)$</field>
    <description>New web script added: $(file)</description>
    <group>web_application,</group>
  </rule>

  <!-- Web file modification -->
  <rule id="100041" level="5">
    <if_sid>550</if_sid>
    <field name="file">/var/www/html/</field>
    <description>Web file modified: $(file)</description>
    <group>web_application,</group>
  </rule>
</group>

Network & Firewall Rules

6. Port Scan Detection

<group name="network,ids,">
  <rule id="100050" level="7">
    <if_sid>510</if_sid>
    <match>Invalid TCP flags|NULL TCP packet|XMAS TCP packet</match>
    <description>Possible port scan technique detected</description>
    <group>network_attack,port_scan,</group>
  </rule>

  <rule id="100051" level="10" frequency="20" timeframe="10">
    <if_matched_sid>100050</if_matched_sid>
    <same_source_ip />
    <description>Intensive port scan detected from same source</description>
    <group>network_attack,port_scan,</group>
  </rule>
</group>

7. Firewall Block Events

<group name="firewall,network,">
  <!-- iptables blocks -->
  <rule id="100060" level="5">
    <program_name>kernel</program_name>
    <match>DPT=</match>
    <regex type="pcre2">IN=.*OUT=.*DPT=(\d+)</regex>
    <description>Firewall blocked packet to port $(port)</description>
    <group>firewall_block,</group>
  </rule>

  <!-- Repeated blocks from same IP -->
  <rule id="100061" level="8" frequency="10" timeframe="60">
    <if_matched_sid>100060</if_matched_sid>
    <same_source_ip />
    <description>Multiple firewall blocks from same source IP</description>
    <group>firewall_block,port_scan,</group>
  </rule>
</group>

Application-Specific Rules

8. Apache Web Server Attacks

<group name="apache,web_application,">
  <!-- SQL Injection attempts -->
  <rule id="100070" level="8">
    <if_sid>31100</if_sid>
    <match>union.*select|select.*from|insert.*into|delete.*from|drop.*table</match>
    <description>Possible SQL injection attempt in web request</description>
    <group>web_attack,sql_injection,</group>
  </rule>

  <!-- Directory traversal attempts -->
  <rule id="100071" level="7">
    <if_sid>31100</if_sid>
    <match>\.\./|\.\.\\|\.\.%2f|\.\.%5c</match>
    <description>Directory traversal attempt in web request</description>
    <group>web_attack,</group>
  </rule>

  <!-- Admin panel access attempts -->
  <rule id="100072" level="5">
    <if_sid>31100</if_sid>
    <match>/admin|/wp-admin|/administrator|/phpmyadmin</match>
    <description>Admin panel access attempt</description>
    <group>web_application,</group>
  </rule>
</group>

9. MySQL/MariaDB Database Monitoring

<group name="mysql,database,">
  <!-- Failed MySQL logins -->
  <rule id="100080" level="5">
    <program_name>mysqld</program_name>
    <match>Access denied for user</match>
    <description>MySQL: Failed login attempt for user $(mysql_user)</description>
    <group>authentication_failed,</group>
  </rule>

  <!-- Successful root MySQL login -->
  <rule id="100081" level="7">
    <program_name>mysqld</program_name>
    <match>root.*localhost.*Connection accepted</match>
    <description>MySQL: Root login detected</description>
    <group>database_access,</group>
  </rule>

  <!-- Multiple failures -->
  <rule id="100082" level="10" frequency="5" timeframe="60">
    <if_matched_sid>100080</if_matched_sid>
    <same_source_ip />
    <description>MySQL: Multiple failed login attempts from same IP</description>
    <group>authentication_failed,brute_force,</group>
  </rule>
</group>

10. Docker Container Events

<group name="docker,container,">
  <!-- Container started -->
  <rule id="100090" level="3">
    <program_name>dockerd</program_name>
    <match>container started</match>
    <description>Docker container started: $(container_name)</description>
    <group>container_event,</group>
  </rule>

  <!-- Privileged container -->
  <rule id="100091" level="8">
    <program_name>dockerd</program_name>
    <match>--privileged</match>
    <description>Privileged container launched: $(container_name)</description>
    <group>container_event,privilege_escalation,</group>
  </rule>

  <!-- Host filesystem mount -->
  <rule id="100092" level="7">
    <program_name>dockerd</program_name>
    <match>-v /:|--volume /:|mount.*/etc|mount.*/var</match>
    <description>Container with host filesystem mount: $(container_name)</description>
    <group>container_event,</group>
  </rule>
</group>

Security & Compliance Rules

11. Account Management

<group name="account_management,">
  <!-- New user created -->
  <rule id="100100" level="5">
    <if_sid>5402</if_sid>
    <match>new user|added.*user</match>
    <description>New user account created: $(username)</description>
    <group>account_change,pci_dss_10.2.5,</group>
  </rule>

  <!-- User deleted -->
  <rule id="100101" level="5">
    <if_sid>5402</if_sid>
    <match>delete user|remove.*user</match>
    <description>User account deleted: $(username)</description>
    <group>account_change,pci_dss_10.2.5,</group>
  </rule>

  <!-- Password changes -->
  <rule id="100102" level="4">
    <program_name>passwd</program_name>
    <match>password changed|password updated</match>
    <description>Password changed for user: $(username)</description>
    <group>account_change,</group>
  </rule>
</group>

12. Cron Job Monitoring

<group name="cron,scheduled_tasks,">
  <!-- New cron job added -->
  <rule id="100110" level="5">
    <program_name>CRON</program_name>
    <match>ADD|REPLACE</match>
    <regex type="pcre2">\((.*)\) ADD|\((.*)\) REPLACE</regex>
    <description>Cron job added/modified for user $(cron_user)</description>
    <group>configuration_change,</group>
  </rule>

  <!-- Suspicious cron commands -->
  <rule id="100111" level="8">
    <if_sid>100110</if_sid>
    <regex type="pcre2">(wget|curl).*(bash|sh)|(nc|netcat).*-e|chmod 777|chmod \+x</regex>
    <description>Suspicious command in cron job</description>
    <group>malicious_activity,</group>
  </rule>
</group>

Advanced Correlation Rules

13. Geographic Anomaly Detection

<group name="geo,anomaly,">
  <!-- Login from unusual location (requires geoip enabled) -->
  <rule id="100120" level="8">
    <if_sid>5710</if_sid>
    <field name="srcgeoip">^(?!US|CA|GB)</field>
    <description>SSH login from non-standard country: $(srcgeoip)</description>
    <group>geo_anomaly,</group>
  </rule>

  <!-- Impossible travel (requires frequency and time) -->
  <rule id="100121" level="12">
    <if_sid>5710</if_sid>
    <field name="srcip" negate="yes">192.168.|10\.|172\.1[6-9]|172\.2[0-9]|172\.3[0-1]</field>
    <description>Login from $(srcgeoip) - requires correlation with previous login</description>
    <group>geo_anomaly,impossible_travel,</group>
  </rule>
</group>

14. Time-Based Anomaly Detection

<group name="time_anomaly,">
  <!-- Logins outside business hours -->
  <rule id="100130" level="5">
    <if_sid>5710</if_sid>
    <time>22:00-06:00</time>
    <weekday>weekdays</weekday>
    <description>SSH login outside business hours for user $(user)</description>
    <group>time_anomaly,</group>
  </rule>

  <!-- Weekend logins -->
  <rule id="100131" level="4">
    <if_sid>5710</if_sid>
    <weekday>saturday,sunday</weekday>
    <description>SSH login on weekend for user $(user)</description>
    <group>time_anomaly,</group>
  </rule>
</group>

15. Data Exfiltration Detection

<group name="data_exfiltration,">
  <!-- Large file downloads -->
  <rule id="100140" level="7">
    <if_sid>31100</if_sid>
    <regex type="pcre2">GET.*\.(tar|gz|zip|rar|7z|sql|bak|old|backup|dump)</regex>
    <description>Potential data exfiltration - archive file download</description>
    <group>data_leak,</group>
  </rule>

  <!-- Outbound connections to suspicious ports -->
  <rule id="100141" level="6">
    <program_name>sshd|kernel</program_name>
    <regex type="pcre2">DPT=(21|22|23|25|53|80|443).*OUT=</regex>
    <description>Outbound connection to $(port) detected</description>
    <group>network_activity,</group>
  </rule>
</group>

Testing Examples with wazuh-logtest

To test these rules, save them to /var/ossec/etc/rules/local_rules.xml and test with sample logs:

# Test SSH failed login
echo "Dec 25 20:45:02 server sshd[12345]: Failed password for root from 192.168.1.100 port 22 ssh2" | /var/ossec/bin/wazuh-logtest

# Test sudo command
echo "Dec 25 20:46:10 server sudo[12346]: user : TTY=pts/0 ; PWD=/home/user ; USER=root ; COMMAND=/bin/su -" | /var/ossec/bin/wazuh-logtest

# Test web attack
echo '192.168.1.100 - - [25/Dec/2024:20:47:15] "GET /index.php?user=1%20union%20select%201,2,3 HTTP/1.1" 200 1234' | /var/ossec/bin/wazuh-logtest

These examples cover common use cases and can be adapted to your specific environment. Remember to adjust rule IDs to avoid conflicts in your environment (use IDs between 100000-120000).

Reference Video:

Leave a Reply

Your email address will not be published. Required fields are marked *