Wazuh

The Wazuh cheat sheet covers SIEM/XDR architecture, agent deployment, custom rules and decoders, MITRE ATT&CK mapping, SCA, FIM, vulnerability detection, threat intelligence, API usage, integrations, and SOC tuning.

#Quick Reference

#Daily SOC Commands

# --- Agent status ---
/var/ossec/bin/agent_control -l               # list all agents
/var/ossec/bin/agent_control -i <id>          # agent detail
/var/ossec/bin/agent_control -R <id>          # restart agent remotely

# --- Service control ---
/var/ossec/bin/wazuh-control start|stop|restart|status|reload

# --- Rule/decoder testing ---
/var/ossec/bin/wazuh-logtest                  # interactive log tester (Wazuh 4.2+)
/var/ossec/bin/wazuh-logtest -v               # verbose (show all decoders tried)
# Legacy name: ossec-logtest (symlinked in some installs)
echo "Apr 10 10:00:00 host sshd[1234]: Failed password for root from 10.0.0.1 port 22 ssh2" \
  | /var/ossec/bin/wazuh-logtest

# --- Real-time alert tailing ---
tail -f /var/ossec/logs/alerts/alerts.log
tail -f /var/ossec/logs/alerts/alerts.json

# --- Config validation ---
/var/ossec/bin/wazuh-analysisd -t             # test ossec.conf syntax

# --- Groups ---
/var/ossec/bin/agent_groups -l                # list groups
/var/ossec/bin/agent_groups -s -i <id>        # show agent group membership

# --- FIM manual scan trigger ---
/var/ossec/bin/agent_control -a -u <id>       # run syscheck on agent

# --- API token (quick) ---
TOKEN=$(curl -su wazuh:wazuh -k -X POST \
  "https://localhost:55000/security/user/authenticate?raw=true")

#Log & Config Locations

Item Path
Manager config /var/ossec/etc/ossec.conf
Agent shared config /var/ossec/etc/shared/<group>/agent.conf
Custom rules /var/ossec/etc/rules/local_rules.xml
Custom decoders /var/ossec/etc/decoders/local_decoder.xml
CDB lists /var/ossec/etc/lists/
SCA policies /var/ossec/etc/shared/
Alerts log /var/ossec/logs/alerts/alerts.log
Alerts JSON /var/ossec/logs/alerts/alerts.json
Archives /var/ossec/logs/archives/archives.log
Manager log /var/ossec/logs/ossec.log
API log /var/ossec/logs/api.log
Rulesets (built-in) /var/ossec/ruleset/rules/
Built-in decoders /var/ossec/ruleset/decoders/

#Rule Creator

Wazuh Rule Creator

Load:

Rule Tester

#Architecture

#Components Overview

Component Role Default Port
Wazuh Manager Core engine: rules, decoders, analysis, alerts 1514 TCP/UDP
Wazuh Agent Endpoint collector: logs, FIM, SCA, inventory -> 1514
Enrollment Service Agent registration & key exchange 1515 TCP
Syslog Collector Agentless: network devices, firewalls 514 UDP
Wazuh Indexer OpenSearch-based alert storage & search 9200, 9300
Wazuh Dashboard OpenSearch Dashboards web UI 443
Filebeat Log shipper: Manager to Indexer internal
Cluster Master Multi-manager orchestration node 1516 TCP
Cluster Worker Distributed processing node 1516 TCP

#Architecture Diagram

  ENDPOINTS                     WAZUH MANAGER                      BACKEND
  ─────────                     ─────────────                      ───────

 [Linux Agent] ─┐               ┌─────────────────────────┐
 [Win Agent  ] ─┤  1514/TCP     │  wazuh-remoted          │       ┌───────────────┐
 [macOS Agent] ─┼──(encrypted)──│  wazuh-analysisd        │──────>│ Wazuh Indexer │
 [Docker Agent] ┘               │  wazuh-maild            │       │ (OpenSearch)  │
                                │  wazuh-monitord         │       │ :9200/:9300   │
 [Firewall   ] ─┐  514/UDP      │  wazuh-logcollector      │       └───────┬───────┘
 [Router     ] ─┤──(syslog)────>│  wazuh-execd            │               │
 [Switch     ] ─┘               │  wazuh-db               │       ┌───────▼───────┐
                                └────────────┬────────────┘       │   Dashboard   │
 [Agentless  ] ──(SSH/SNMP)────>             │                    │   :443        │
                                        Filebeat                  └───────────────┘
                                             │
                                     ┌───────▼───────┐
                                     │  Active        │
                                     │  Response      │
                                     │  (firewall-    │
                                     │   drop, etc.)  │
                                     └───────────────┘

  CLUSTER (optional):
  [Master :1516] <──────────────> [Worker :1516] <── Agents

#Agent Installation

#Linux: Debian/Ubuntu

# Import GPG key
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH \
  | gpg --no-default-keyring \
    --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg \
    --import
chmod 644 /usr/share/keyrings/wazuh.gpg

# Add repository
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] \
  https://packages.wazuh.com/4.x/apt/ stable main" \
  | tee /etc/apt/sources.list.d/wazuh.list

# Install with manager address pre-set
apt-get update
WAZUH_MANAGER="10.0.0.1" apt-get install -y wazuh-agent

# Enable and start
systemctl daemon-reload
systemctl enable --now wazuh-agent

#Linux: RHEL/CentOS/Amazon

# Add repo
cat > /etc/yum.repos.d/wazuh.repo << 'EOF'
[wazuh]
gpgcheck=1
gpgkey=https://packages.wazuh.com/key/GPG-KEY-WAZUH
enabled=1
name=EL-$releasever - Wazuh
baseurl=https://packages.wazuh.com/4.x/yum/
protect=1
EOF

# Install
WAZUH_MANAGER="10.0.0.1" yum install -y wazuh-agent

systemctl daemon-reload
systemctl enable --now wazuh-agent

#Windows Agent

# Download installer
Invoke-WebRequest `
  -Uri "https://packages.wazuh.com/4.x/windows/wazuh-agent-4.x.x-1.msi" `
  -OutFile "$env:TEMP\wazuh-agent.msi"

# Silent install with manager and group
msiexec /i "$env:TEMP\wazuh-agent.msi" /q `
  WAZUH_MANAGER="10.0.0.1" `
  WAZUH_AGENT_GROUP="windows-servers" `
  WAZUH_REGISTRATION_PASSWORD="MySecret"

# Start service
NET START WazuhSvc

# Verify
Get-Service WazuhSvc

#macOS Agent

# Download pkg
curl -so wazuh-agent.pkg \
  "https://packages.wazuh.com/4.x/macos/wazuh-agent-4.x.x-1.pkg"

# Pre-configure manager
echo "WAZUH_MANAGER='10.0.0.1'" > /tmp/wazuh_envs
echo "WAZUH_AGENT_GROUP='macos'" >> /tmp/wazuh_envs

# Install
installer -pkg wazuh-agent.pkg -target /

# Start
/Library/Ossec/bin/wazuh-control start

#Docker Agent

docker run -d --name wazuh-agent \
  --restart unless-stopped \
  -e WAZUH_MANAGER="10.0.0.1" \
  -e WAZUH_AGENT_GROUP="docker-hosts" \
  -e WAZUH_REGISTRATION_PASSWORD="MySecret" \
  -v /var/log:/var/log:ro \
  -v /etc:/etc:ro \
  --pid=host \
  --network=host \
  --privileged \
  wazuh/wazuh/wazuh-agent:latest

#Manual Registration (agent-auth)

# On agent: register with manager enrollment service (port 1515)
/var/ossec/bin/agent-auth \
  -m 10.0.0.1 \
  -p 1515 \
  -G linux-servers \
  -P "EnrollmentPassword"

# Verify key was issued
cat /var/ossec/etc/client.keys

#Agent Groups

#Managing Groups

# Create group
/var/ossec/bin/agent_groups -a -g linux-servers -q
/var/ossec/bin/agent_groups -a -g windows-servers -q
/var/ossec/bin/agent_groups -a -g dmz -q

# List all groups
/var/ossec/bin/agent_groups -l

# Assign agent to group
/var/ossec/bin/agent_groups -a -i 003 -g linux-servers -q

# Show group members
/var/ossec/bin/agent_groups -s -g linux-servers

# Remove agent from group
/var/ossec/bin/agent_groups -r -i 003 -g linux-servers -q

# API: create group
curl -k -X POST "https://localhost:55000/groups" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"group_id":"linux-servers"}'

# API: assign agent to group
curl -k -X PUT \
  "https://localhost:55000/agents/003/group/linux-servers" \
  -H "Authorization: Bearer $TOKEN"

#Group-Specific Configuration

<!-- /var/ossec/etc/shared/linux-servers/agent.conf -->
<!-- Pushed automatically to all agents in the group -->
<agent_config>

  <!-- Extra log collection for this group -->
  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/auth.log</location>
  </localfile>

  <!-- Group-specific FIM paths -->
  <syscheck>
    <directories realtime="yes" whodata="yes">/opt/app</directories>
    <directories check_all="yes" report_changes="yes">/etc/nginx</directories>
  </syscheck>

  <!-- Group-specific SCA policy -->
  <sca>
    <policies>
      <policy>etc/shared/cis_debian_linux_rcl.yml</policy>
    </policies>
  </sca>

</agent_config>

#Key Configuration Files

#ossec.conf - Manager Core

<!-- /var/ossec/etc/ossec.conf -->
<ossec_config>

  <global>
    <email_notification>yes</email_notification>
    <email_to>[email protected]</email_to>
    <smtp_server>mail.company.com</smtp_server>
    <email_from>[email protected]</email_from>
    <email_maxperhour>12</email_maxperhour>
    <email_log_source>alerts.log</email_log_source>
    <agents_disconnection_time>10m</agents_disconnection_time>
    <agents_disconnection_alert_time>0</agents_disconnection_alert_time>
  </global>

  <!-- Ruleset paths -->
  <ruleset>
    <decoder_dir>ruleset/decoders</decoder_dir>
    <rule_dir>ruleset/rules</rule_dir>
    <rule_exclude>0215-policy_rules.xml</rule_exclude>
    <list>etc/lists/audit-keys</list>
    <list>etc/lists/amazon/aws-eventnames</list>
    <!-- Custom lists for threat intel -->
    <list>etc/lists/malicious-ips</list>
    <list>etc/lists/malicious-hashes</list>
  </ruleset>

  <!-- Remote agent connection -->
  <remote>
    <connection>secure</connection>
    <port>1514</port>
    <protocol>tcp</protocol>
    <queue_size>131072</queue_size>
  </remote>

  <!-- Alerts config -->
  <alerts>
    <log_alert_level>3</log_alert_level>
    <email_alert_level>12</email_alert_level>
  </alerts>

</ossec_config>

#Agent ossec.conf Key Sections

<!-- /var/ossec/etc/ossec.conf (on agent) -->
<ossec_config>
  <client>
    <server>
      <address>10.0.0.1</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <config-profile>ubuntu, ubuntu22</config-profile>
    <notify_time>10</notify_time>
    <time-reconnect>60</time-reconnect>
    <auto_restart>yes</auto_restart>
    <enrollment>
      <enabled>yes</enabled>
      <manager_address>10.0.0.1</manager_address>
      <port>1515</port>
    </enrollment>
  </client>

  <!-- Windows event channels -->
  <localfile>
    <log_format>eventchannel</log_format>
    <location>Security</location>
    <query>Event/System[EventID != 4688]</query>
  </localfile>
  <localfile>
    <log_format>eventchannel</log_format>
    <location>Microsoft-Windows-Sysmon/Operational</location>
  </localfile>
  <localfile>
    <log_format>eventchannel</log_format>
    <location>Microsoft-Windows-PowerShell/Operational</location>
  </localfile>
</ossec_config>

#Custom Rules

#Rule Field Reference

Field Type Description
id attr Unique rule ID (100000-999999 for custom)
level attr Alert severity 0-15
frequency attr Times rule must match before alert fires
timeframe attr Seconds window for frequency counting
overwrite attr yes to replace existing built-in rule
noalert attr 1 to fire but suppress alert output
<if_sid> elem Match if a prior rule ID has fired
<if_group> elem Match if prior event was in this group
<if_level> elem Match if prior event reached this level
<if_matched_sid> elem Frequency match: prior rule fired N times in timeframe
<if_matched_group> elem Frequency match on group within timeframe
<match> elem Substring/sregex match against full log
<regex> elem POSIX regex against full log
<field name=""> elem Match against decoded field (supports negate, PCRE2)
<srcip> elem Source IP or CIDR match
<dstip> elem Destination IP or CIDR match
<user> elem Username match (supports negate)
<program_name> elem Process name match
<hostname> elem Agent hostname match
<url> elem URL field match
<status> elem Status field match
<action> elem Action field match
<same_srcip> elem Correlation: same source IP across events
<different_user> elem Correlation: different user across events
<list field=""> elem CDB list lookup for threat intel
<description> elem Human-readable alert message
<group> elem Comma-separated alert categories
<mitre><id> elem MITRE ATT&CK technique mapping
<options> elem Flags: no_full_log, no_log, alert_by_email

#Rule Structure & Correlation

<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="local,custom,">

  <!-- ── BASIC: match string in decoded field ── -->
  <rule id="100001" level="6">
    <if_group>syslog</if_group>
    <match>sudo: authentication failure</match>
    <description>Failed sudo attempt</description>
    <group>authentication_failure,pam,</group>
  </rule>

  <!-- ── COMPOSITE: brute force (5 failures / 120 sec from same IP) ── -->
  <rule id="100002" level="12" frequency="5" timeframe="120">
    <if_matched_sid>100001</if_matched_sid>
    <same_srcip />
    <description>Brute force: multiple sudo failures from $(srcip)</description>
    <group>authentication_failures,brute_force,</group>
    <mitre>
      <id>T1110</id>
    </mitre>
  </rule>

  <!-- ── FIELD: match specific decoded field with PCRE2 ── -->
  <rule id="100003" level="10">
    <if_group>web</if_group>
    <field name="url" type="pcre2">(?i)\.(php|asp|aspx|jsp)\?.*=.*(\.\./|cmd=|exec=)</field>
    <status>200</status>
    <description>Web app exploit attempt: LFI/RFI or command injection</description>
    <group>web_attack,attack,</group>
    <mitre>
      <id>T1190</id>
    </mitre>
  </rule>

  <!-- ── NEGATION: alert if sshd event is NOT from allowed subnet ── -->
  <rule id="100004" level="8">
    <if_group>sshd</if_group>
    <action>^Accepted</action>
    <srcip negate="yes">10.0.0.0/8</srcip>
    <srcip negate="yes">192.168.0.0/16</srcip>
    <description>SSH login from unexpected source: $(srcip)</description>
    <group>authentication_success,anomaly,</group>
    <mitre>
      <id>T1078</id>
    </mitre>
  </rule>

  <!-- ── CDB LIST: alert if srcip is in threat intel list ── -->
  <rule id="100005" level="14">
    <if_group>firewall</if_group>
    <list field="srcip" lookup="address_match_key">etc/lists/malicious-ips</list>
    <description>Connection from known malicious IP: $(srcip)</description>
    <group>threat_intel,attack,</group>
    <mitre>
      <id>T1071</id>
    </mitre>
  </rule>

  <!-- ── OVERWRITE: lower level of noisy built-in rule ── -->
  <rule id="5710" level="0" overwrite="yes">
    <if_sid>5710</if_sid>
    <description>SSH failed login (suppressed - handled by custom rule)</description>
  </rule>

</group>

#Alert Level Guide

Level Severity Use Case Example
0 Ignored Suppress noisy events Routine cron output
1-3 Informational Low-value events Successful logins, normal ops
4-6 Low Noteworthy activity Config changes, new connections
7-9 Medium Security-relevant events Failed logins, FIM changes
10-11 High Attack indicators Privilege escalation attempts
12-13 Critical Active attack Brute force, exploit attempts
14-15 Maximum Confirmed compromise Rootkit, credential dump

#Detection Rules: ATT&CK Coverage

#10 Ready-to-Use Detection Rules

<group name="threat_detection,custom,">

  <!-- T1003 - Mimikatz / credential dumping -->
  <rule id="110001" level="15">
    <if_group>windows</if_group>
    <field name="win.eventdata.commandLine" type="pcre2">
      (?i)(sekurlsa|lsadump|privilege::debug|token::elevate|kerberos::ptt|dpapi::)
    </field>
    <description>Mimikatz execution detected: $(win.eventdata.commandLine)</description>
    <group>credential_access,attack,</group>
    <mitre><id>T1003</id></mitre>
  </rule>

  <!-- T1550.002 - Pass-the-Hash (EventID 4624 logon type 3 with NtLm) -->
  <rule id="110002" level="14">
    <if_group>windows</if_group>
    <field name="win.system.eventID">^4624$</field>
    <field name="win.eventdata.logonType">^3$</field>
    <field name="win.eventdata.authenticationPackageName">^NTLM$</field>
    <field name="win.eventdata.workstationName" negate="yes">$(win.eventdata.targetDomainName)</field>
    <description>Possible Pass-the-Hash: NTLM Type3 from $(win.eventdata.ipAddress)</description>
    <group>lateral_movement,credential_access,</group>
    <mitre><id>T1550.002</id></mitre>
  </rule>

  <!-- T1021.002 - PsExec lateral movement (Sysmon EventID 1 or SC service creation) -->
  <rule id="110003" level="13">
    <if_group>windows</if_group>
    <field name="win.eventdata.commandLine" type="pcre2">(?i)(psexec|psexesvc)</field>
    <description>PsExec execution detected on $(agent.name)</description>
    <group>lateral_movement,attack,</group>
    <mitre><id>T1021.002</id></mitre>
  </rule>

  <!-- T1041 - Data exfiltration over HTTP (large POST to external IP) -->
  <rule id="110004" level="12">
    <if_group>web_log</if_group>
    <field name="http.method">^POST$</field>
    <field name="http.response_bytes" type="pcre2">^[1-9][0-9]{6,}$</field>
    <description>Large outbound POST - possible data exfiltration: $(srcip)</description>
    <group>exfiltration,attack,</group>
    <mitre><id>T1041</id></mitre>
  </rule>

  <!-- T1505.003 - Webshell creation detected by FIM -->
  <rule id="110005" level="15">
    <if_group>syscheck</if_group>
    <field name="file" type="pcre2">(?i)\.(php|asp|aspx|jsp|cfm)$</field>
    <field name="syscheck.event">added</field>
    <field name="file" type="pcre2">(?i)(www|html|public_html|htdocs|webroot)</field>
    <description>Webshell possibly dropped in web root: $(file)</description>
    <group>web_attack,attack,</group>
    <mitre><id>T1505.003</id></mitre>
  </rule>

  <!-- T1078.003 - Privilege escalation: new sudoer added -->
  <rule id="110006" level="14">
    <if_group>syscheck</if_group>
    <field name="file">/etc/sudoers|/etc/sudoers.d/</field>
    <field name="syscheck.event">modified|added</field>
    <description>Sudoers file modified - possible privilege escalation</description>
    <group>privilege_escalation,attack,</group>
    <mitre><id>T1078.003</id></mitre>
  </rule>

  <!-- T1053.005 - Scheduled task created (Windows EventID 4698) -->
  <rule id="110007" level="12">
    <if_group>windows</if_group>
    <field name="win.system.eventID">^4698$</field>
    <description>Scheduled task created: $(win.eventdata.taskName) by $(win.eventdata.subjectUserName)</description>
    <group>persistence,attack,</group>
    <mitre><id>T1053.005</id></mitre>
  </rule>

  <!-- T1136.001 - New local user created (Windows EventID 4720) -->
  <rule id="110008" level="11">
    <if_group>windows</if_group>
    <field name="win.system.eventID">^4720$</field>
    <description>New Windows user created: $(win.eventdata.targetUserName) by $(win.eventdata.subjectUserName)</description>
    <group>persistence,account_management,</group>
    <mitre><id>T1136.001</id></mitre>
  </rule>

  <!-- T1070.001 - Security log cleared (Windows EventID 1102) -->
  <rule id="110009" level="14">
    <if_group>windows</if_group>
    <field name="win.system.eventID">^1102$</field>
    <description>Windows Security event log cleared by $(win.eventdata.subjectUserName)</description>
    <group>defense_evasion,attack,</group>
    <mitre><id>T1070.001</id></mitre>
  </rule>

  <!-- T1059.001 - Suspicious PowerShell (encoded commands / download cradles) -->
  <rule id="110010" level="13">
    <if_group>windows</if_group>
    <field name="win.eventdata.commandLine" type="pcre2">
      (?i)(-enc|-encodedcommand|iex\s*\(|invoke-expression|downloadstring|webclient|bitstransfer)
    </field>
    <description>Suspicious PowerShell execution: $(win.eventdata.commandLine)</description>
    <group>execution,attack,</group>
    <mitre><id>T1059.001</id></mitre>
  </rule>

</group>

#Custom Decoders

#Decoder Field Reference

Field Description
<prematch> Regex the log must match before field extraction; anchors decoder to log type
<program_name> Match against syslog program name field
<parent> Links child to parent decoder; child runs only if parent matched
<regex> POSIX/PCRE2 with capture groups () for field extraction
<order> Comma-separated field names mapping to regex capture groups
<type> Log category: syslog, firewall, ids, web-log, windows, ossec
<accumulate> Correlate multi-line events via decoded id field
<plugin_decoder> Use built-in parsers: JSON_Decoder, PF_Decoder, OSSECAlert_Decoder
<fts> First-time-seen: alert on first occurrence of field value
<var name=""> Define reusable regex variable at file level

#Parent/Child Decoder Pattern

<!-- /var/ossec/etc/decoders/local_decoder.xml -->

<!-- ── Variable definition ── -->
<decoder-list>
  <var name="IP_REGEX">(?:\d{1,3}\.){3}\d{1,3}</var>
</decoder-list>

<!-- ── Parent: identifies the log source ── -->
<decoder name="myapp">
  <program_name>^myapp$</program_name>
  <prematch>^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}</prematch>
  <type>syslog</type>
</decoder>

<!-- ── Child: extracts fields from auth events ── -->
<decoder name="myapp-auth-success">
  <parent>myapp</parent>
  <prematch>AUTH_SUCCESS</prematch>
  <regex>User (\S+) authenticated from ($IP_REGEX) port (\d+)</regex>
  <order>user, srcip, srcport</order>
</decoder>

<!-- ── Child: extracts fields from auth failures ── -->
<decoder name="myapp-auth-fail">
  <parent>myapp</parent>
  <prematch>AUTH_FAILED</prematch>
  <regex>Failed login for (\S+) from ($IP_REGEX): (\S+)</regex>
  <order>user, srcip, extra_data</order>
</decoder>

<!-- ── JSON decoder (for structured log output) ── -->
<decoder name="myapp-json">
  <parent>myapp</parent>
  <prematch>{</prematch>
  <plugin_decoder>JSON_Decoder</plugin_decoder>
</decoder>

<!-- Test: echo '2024-01-01T10:00:00 AUTH_SUCCESS User bob authenticated from 10.0.0.5 port 52001' | /var/ossec/bin/wazuh-logtest -->

#MITRE ATT&CK Integration

#Mapping Rules to Techniques

<!-- Single technique -->
<rule id="120001" level="13">
  <if_group>syslog</if_group>
  <match>su: FAILED</match>
  <description>Failed su command</description>
  <mitre>
    <id>T1548.003</id>
  </mitre>
</rule>

<!-- Multiple techniques in one rule -->
<rule id="120002" level="14">
  <if_group>windows</if_group>
  <field name="win.eventdata.commandLine" type="pcre2">
    (?i)(net\s+user|net\s+localgroup|dsquery|Get-ADUser)
  </field>
  <description>Active Directory enumeration detected</description>
  <mitre>
    <id>T1087.002</id>   <!-- Account Discovery: Domain Account -->
    <id>T1069.002</id>   <!-- Permission Groups Discovery: Domain Groups -->
  </mitre>
</rule>

#ATT&CK Dashboard Queries

# Kibana/OpenSearch Dashboards - filter by technique
rule.mitre.id: "T1059.001"

# All credential access techniques
rule.mitre.tactic: "Credential Access"

# Top MITRE techniques (last 24h)
# Dashboard > MITRE ATT&CK > Framework tab > filter timerange

# API: get alerts by MITRE technique
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/alerts?q=rule.mitre.id=T1059.001&limit=100&pretty=true"

#MITRE ATT&CK Tactic-to-Technique Map

Tactic Common Techniques Wazuh Rule Groups
Initial Access T1190, T1133, T1566 web_attack, phishing
Execution T1059, T1053, T1204 execution, windows
Persistence T1053, T1136, T1543 persistence, account_management
Privilege Escalation T1548, T1078, T1134 privilege_escalation
Defense Evasion T1070, T1562, T1055 defense_evasion
Credential Access T1003, T1110, T1558 credential_access, brute_force
Lateral Movement T1021, T1550, T1563 lateral_movement
Exfiltration T1041, T1048, T1052 exfiltration

#File Integrity Monitoring (FIM)

#FIM Configuration Reference

<!-- In ossec.conf or agent.conf -->
<syscheck>

  <!-- Scan interval (seconds); default 43200 = 12h -->
  <frequency>43200</frequency>

  <!-- Scheduled scan at specific time -->
  <scan_time>02:00</scan_time>
  <scan_day>saturday</scan_day>

  <!-- Disable at startup -->
  <scan_on_start>yes</scan_on_start>

  <!-- ── Realtime monitoring (inotify/FSEvents) ── -->
  <directories realtime="yes">/var/www/html</directories>

  <!-- ── Whodata: who changed the file (audit subsystem) ── -->
  <directories whodata="yes" realtime="yes">/etc</directories>

  <!-- ── Full options ── -->
  <directories
    check_all="yes"
    report_changes="yes"
    realtime="yes"
    whodata="yes"
    recursion_level="3"
    tags="critical_config"
  >/etc/ssh,/etc/pam.d,/etc/sudoers.d</directories>

  <!-- ── Windows: critical system paths ── -->
  <directories check_all="yes" realtime="yes">%WINDIR%\System32\drivers</directories>
  <directories check_all="yes" realtime="yes">%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup</directories>

  <!-- ── Ignore patterns ── -->
  <ignore>/etc/mtab</ignore>
  <ignore>/etc/mnttab</ignore>
  <ignore type="sregex">\.log$|\.tmp$|\.swp$</ignore>

  <!-- ── Nodiff: suppress content diff for sensitive files ── -->
  <nodiff>/etc/ssl/private</nodiff>
  <nodiff type="sregex">\.key$|\.pem$|id_rsa</nodiff>

  <!-- ── Windows Registry monitoring ── -->
  <windows_registry>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run</windows_registry>
  <windows_registry arch="both">HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services</windows_registry>

  <!-- ── Registry ignore ── -->
  <registry_ignore>HKEY_LOCAL_MACHINE\Security\Policy\Secrets</registry_ignore>

  <!-- ── Restrict: only watch specific file types ── -->
  <directories restrict="\.conf$|\.cfg$|\.ini$">/opt/app/config</directories>

</syscheck>

#FIM Mode Comparison

Mode Trigger CPU Impact Use Case
scheduled Periodic scan (frequency) Low Non-critical paths
realtime inotify/FSEvents event Medium Web roots, critical configs
whodata Audit daemon + inotify Higher Forensic-grade: user + PID tracked

#SCA - Security Configuration Assessment

#SCA ossec.conf Settings

<!-- In ossec.conf or agent.conf -->
<sca>
  <enabled>yes</enabled>
  <!-- Scan interval -->
  <interval>12h</interval>
  <scan_on_start>yes</scan_on_start>
  <!-- Skip failed policies -->
  <skip_nfs>yes</skip_nfs>
  <policies>
    <!-- Enable specific built-in policies -->
    <policy>etc/shared/cis_debian_linux_rcl.yml</policy>
    <policy>etc/shared/cis_rhel9_linux_rcl.yml</policy>
    <!-- Custom policy -->
    <policy>etc/shared/custom_hardening.yml</policy>
    <!-- Disable a policy -->
    <policy enabled="no">etc/shared/web_vulnerabilities.yml</policy>
  </policies>
</sca>

#Custom SCA Policy Format

# /var/ossec/etc/shared/custom_hardening.yml
policy:
  id: "custom_linux_hardening"
  file: "custom_hardening.yml"
  name: "Custom Linux Hardening Policy"
  description: "Organization-specific security checks"
  references:
    - "https://wiki.internal/hardening"

requirements:
  title: "Check Linux is installed"
  description: "Requirements to run this policy"
  condition: all
  rules:
    - 'f:/etc/os-release'

variables:
  $sshd_config: /etc/ssh/sshd_config
  $pam_config: /etc/pam.d/common-password

checks:

  - id: 1000
    title: "SSH root login is disabled"
    description: "Ensure PermitRootLogin is set to no"
    rationale: "Preventing direct root login reduces attack surface"
    remediation: "Set PermitRootLogin no in /etc/ssh/sshd_config and restart sshd"
    compliance:
      - cis: "5.2.8"
      - pci_dss: "2.2.4"
    condition: all
    rules:
      - 'f:$sshd_config -> !r:^# && r:PermitRootLogin && r:no'

  - id: 1001
    title: "SSH MaxAuthTries is 4 or less"
    condition: all
    rules:
      - 'f:$sshd_config -> n:MaxAuthTries\s+(\d+) compare <= 4'

  - id: 1002
    title: "No accounts with empty passwords"
    condition: none
    rules:
      - 'f:/etc/shadow -> r:^\w+::+'

  - id: 1003
    title: "Firewall is active"
    condition: any
    rules:
      - 'p:ufw'
      - 'p:firewalld'
      - 'c:iptables -L -> r:Chain INPUT'

  - id: 1004
    title: "Audit daemon is running"
    condition: all
    rules:
      - 'p:auditd'

  - id: 2000
    title: "Windows: SMBv1 is disabled"
    condition: all
    rules:
      - 'r:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters -> SMB1 -> 0'

#SCA API Queries

# Get SCA summary for all agents
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/sca?pretty=true"

# Get SCA results for specific agent
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/sca/003?pretty=true"

# Get failed checks for an agent
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/sca/003/checks/custom_linux_hardening?result=failed&pretty=true"

#Vulnerability Detection

#Vulnerability Detection Config

<!-- In /var/ossec/etc/ossec.conf on manager -->
<vulnerability-detection>
  <enabled>yes</enabled>
  <!-- Index vulnerability data to Wazuh Indexer -->
  <index-status>yes</index-status>
  <!-- Feed update interval (min 60m) -->
  <feed-update-interval>12h</feed-update-interval>
</vulnerability-detection>

<!-- Syscollector: must be enabled on agents to collect inventory -->
<wodle name="syscollector">
  <disabled>no</disabled>
  <interval>1h</interval>
  <scan_on_start>yes</scan_on_start>
  <packages>yes</packages>
  <os>yes</os>
  <processes>yes</processes>
  <ports all="no">yes</ports>
  <network>yes</network>
</wodle>

#Vulnerability API Queries

# Get all vulnerabilities for an agent
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/vulnerability/003?pretty=true"

# Filter by severity
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/vulnerability/003?severity=critical&pretty=true"

# Get CVE details
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/vulnerability/003?cve=CVE-2021-44228&pretty=true"

# Get all agents with critical vulns
curl -k -H "Authorization: Bearer $TOKEN" \
  "https://localhost:55000/vulnerability?severity=critical&limit=500&pretty=true"

# Dashboard KQL - find critical vulns
# vulnerability.severity: "critical" AND vulnerability.status: "active"

#Syscollector Fields in Indexer

vulnerability.cve           - CVE identifier
vulnerability.severity      - critical/high/medium/low
vulnerability.cvss2_score   - CVSS v2 score
vulnerability.cvss3_score   - CVSS v3 score
vulnerability.package.name  - Affected package
vulnerability.package.version - Installed version
vulnerability.status        - active/solved

#REST API

#Authentication & Setup

# Get JWT token
TOKEN=$(curl -su wazuh:wazuh -k -X POST \
  "https://localhost:55000/security/user/authenticate?raw=true")

# Or with custom credentials
TOKEN=$(curl -su myuser:mypass -k -X POST \
  "https://localhost:55000/security/user/authenticate?raw=true")

# All requests use: -H "Authorization: Bearer $TOKEN"
# API config: /var/ossec/api/configuration/api.yaml
# Default port: 55000

#Core API Endpoints

Method Endpoint Description
GET /agents List all agents with status
GET /agents/summary/status Agent count by status
GET /agents/{id} Specific agent detail
PUT /agents/{id}/restart Restart an agent
DELETE /agents Remove agents (query param: agents_list)
GET /agents/{id}/config/{component}/{configuration} Get agent config
GET /groups List all agent groups
POST /groups Create group
PUT /agents/{id}/group/{group_id} Assign agent to group
GET /rules List detection rules
GET /rules?search=mimikatz Search rules
GET /decoders List decoders
GET /alerts Query alerts
GET /syscheck/{id} FIM events for agent
PUT /syscheck/{id} Run FIM scan on agent
GET /sca/{id} SCA results for agent
GET /vulnerability/{id} Vulnerability scan results
GET /manager/info Manager version and info
GET /manager/logs Manager log tail
PUT /manager/restart Restart manager
GET /cluster/nodes Cluster node status

#Practical API Examples

BASE="https://localhost:55000"
H="Authorization: Bearer $TOKEN"

# --- Agents ---
# List active agents
curl -sk -H "$H" "$BASE/agents?status=active&pretty=true"

# Get disconnected agents
curl -sk -H "$H" "$BASE/agents?status=disconnected&select=id,name,lastKeepAlive&pretty=true"

# Get agent OS inventory
curl -sk -H "$H" "$BASE/syscollector/003/os?pretty=true"

# Get agent packages
curl -sk -H "$H" "$BASE/syscollector/003/packages?pretty=true&limit=100"

# Get agent processes
curl -sk -H "$H" "$BASE/syscollector/003/processes?pretty=true"

# Restart ALL agents
curl -sk -H "$H" -X PUT "$BASE/agents/restart?agents_list=all"

# --- Rules ---
# Find rules by group
curl -sk -H "$H" "$BASE/rules?group=authentication_failure&pretty=true"

# Get rule by ID
curl -sk -H "$H" "$BASE/rules?rule_ids=100001&pretty=true"

# --- Alerts ---
# Recent 50 critical alerts
curl -sk -H "$H" "$BASE/alerts?level=14&limit=50&pretty=true"

# --- FIM ---
# Recent FIM changes for agent
curl -sk -H "$H" "$BASE/syscheck/003?sort=-date&limit=50&pretty=true"

# FIM events filtered by path
curl -sk -H "$H" "$BASE/syscheck/003?q=file=/etc/passwd&pretty=true"

#Integrations

#Slack Notifications

<!-- In /var/ossec/etc/ossec.conf -->
<integration>
  <name>slack</name>
  <hook_url>https://hooks.slack.com/services/T00/B00/XXXXXXX</hook_url>
  <!-- Only send alerts level 10+ -->
  <level>10</level>
  <alert_format>json</alert_format>
</integration>

<!-- Scope to specific rule groups -->
<integration>
  <name>slack</name>
  <hook_url>https://hooks.slack.com/services/T00/B00/XXXXXXX</hook_url>
  <group>authentication_failures,web_attack,attack</group>
  <alert_format>json</alert_format>
</integration>

#VirusTotal Integration

<!-- FIM + VirusTotal: check file hashes on modification -->
<integration>
  <name>virustotal</name>
  <api_key>YOUR_VT_API_KEY</api_key>
  <!-- Must use syscheck group -->
  <group>syscheck</group>
  <alert_format>json</alert_format>
</integration>

<!-- Trigger rule for VT positive result (auto-generated by integration) -->
<!-- Rule 87105: VirusTotal alert - X engines detected the file -->

#Email Alerts

<global>
  <email_notification>yes</email_notification>
  <email_to>[email protected]</email_to>
  <smtp_server>mail.company.com</smtp_server>
  <email_from>[email protected]</email_from>
  <email_maxperhour>20</email_maxperhour>
  <!-- Granular: send email for specific rules regardless of level -->
  <email_alert_level>12</email_alert_level>
</global>

<!-- Per-rule email override in alert section -->
<email_alerts>
  <email_to>[email protected]</email_to>
  <rule_id>110001,110009</rule_id>
  <do_not_delay />
</email_alerts>

#TheHive Integration

<integration>
  <name>custom-w2thive</name>
  <!-- Path to custom Python script -->
  <hook_url>http://thehive.company.com:9000</hook_url>
  <api_key>YOUR_THEHIVE_API_KEY</api_key>
  <level>10</level>
  <alert_format>json</alert_format>
</integration>

<!-- Custom script at: /var/ossec/integrations/custom-w2thive -->
<!-- chmod 750 /var/ossec/integrations/custom-w2thive -->
<!-- chown root:wazuh /var/ossec/integrations/custom-w2thive -->

#PagerDuty / Generic Webhook

<integration>
  <name>pagerduty</name>
  <api_key>PAGERDUTY_ROUTING_KEY</api_key>
  <level>12</level>
  <alert_format>json</alert_format>
</integration>

#Active Response

#Configuration Reference

<!-- In /var/ossec/etc/ossec.conf (Manager) -->

<!-- Step 1: define command -->
<command>
  <name>firewall-drop</name>
  <executable>firewall-drop</executable>
  <!-- Allow timeout-based reversal -->
  <timeout_allowed>yes</timeout_allowed>
</command>

<command>
  <name>disable-account</name>
  <executable>disable-account</executable>
  <timeout_allowed>yes</timeout_allowed>
</command>

<!-- Custom Python/Bash script -->
<command>
  <name>isolate-host</name>
  <executable>isolate-host.sh</executable>
  <timeout_allowed>yes</timeout_allowed>
</command>

<!-- Step 2: bind command to trigger -->
<active-response>
  <command>firewall-drop</command>
  <!-- local=triggering agent | server=manager | all=every agent | defined-agent=specific ID -->
  <location>local</location>
  <!-- Trigger on specific rule IDs -->
  <rules_id>100002,5712,5716</rules_id>
  <!-- Or trigger on level -->
  <!-- <level>12</level> -->
  <!-- Or trigger on group -->
  <!-- <rules_group>authentication_failures</rules_group> -->
  <!-- Unblock after 3600 seconds -->
  <timeout>3600</timeout>
</active-response>

<active-response>
  <command>disable-account</command>
  <location>local</location>
  <rules_id>110008</rules_id>
  <timeout>3600</timeout>
</active-response>

#Built-in AR Scripts

Script Effect Platform
firewall-drop Block srcip with iptables/nftables Linux
ip-customblock Customizable IP block Linux
disable-account Lock user account Linux/Windows
restart-wazuh Restart Wazuh agent service Any
netsh Windows Firewall block rule Windows
route-null Null-route IP via routing table Linux
wazuh-slack Send Slack notification Any
kaspersky Kaspersky AV scan trigger Windows

#Tuning & False Positive Suppression

#Overwriting Built-in Rules

<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="tuning,">

  <!-- Lower level of noisy rule (overwrite="yes" replaces built-in) -->
  <rule id="5712" level="3" overwrite="yes">
    <if_sid>5712</if_sid>
    <description>SSH failed login (downgraded - high volume environment)</description>
  </rule>

  <!-- Completely suppress a rule -->
  <rule id="5715" level="0" overwrite="yes">
    <if_sid>5715</if_sid>
    <description>SSH reverse mapping (suppressed)</description>
  </rule>

  <!-- Suppress for specific user -->
  <rule id="100900" level="0">
    <if_sid>5710</if_sid>
    <user>backup-svc</user>
    <description>SSH failure from service account (expected)</description>
  </rule>

  <!-- Suppress for specific IP -->
  <rule id="100901" level="0">
    <if_sid>5710</if_sid>
    <srcip>10.0.0.50</srcip>
    <description>SSH failure from monitoring host (expected)</description>
  </rule>

  <!-- Raise level of specific event for VIP host -->
  <rule id="100902" level="15" overwrite="yes">
    <if_sid>5710</if_sid>
    <hostname>dc01.corp.local</hostname>
    <description>SSH failure on domain controller - elevated priority</description>
  </rule>

</group>

#CDB List: IP Whitelisting & Threat Intel

# Create whitelist file
cat > /var/ossec/etc/lists/whitelist-ips << 'EOF'
10.0.0.50:scanner
10.0.0.51:monitoring
192.168.1.100:pentest-approved
EOF

# Create malicious IP list
cat > /var/ossec/etc/lists/malicious-ips << 'EOF'
185.220.101.1:c2
45.33.32.156:scanner
EOF

# Register lists in ossec.conf ruleset block
# <list>etc/lists/whitelist-ips</list>
# <list>etc/lists/malicious-ips</list>

# Rebuild CDB binary index (automatic on manager restart)
# wazuh-analysisd re-reads lists on start/restart
/var/ossec/bin/wazuh-control restart
# Verify list loaded:
grep "lists" /var/ossec/logs/ossec.log | tail -5
<!-- Use whitelist in rule to suppress -->
<rule id="100910" level="0">
  <if_sid>5710,5711,5712</if_sid>
  <list field="srcip" lookup="address_match_key">etc/lists/whitelist-ips</list>
  <description>SSH failure from whitelisted host - suppressed</description>
</rule>

<!-- Use threat intel list to escalate -->
<rule id="100911" level="15">
  <if_group>syslog,firewall,web</if_group>
  <list field="srcip" lookup="address_match_key">etc/lists/malicious-ips</list>
  <description>Traffic from known malicious IP: $(srcip)</description>
  <group>threat_intel,attack,</group>
  <mitre><id>T1071</id></mitre>
</rule>

#ossec.conf Global Tuning

<global>
  <!-- Ignore repeated alerts from same IP within N seconds -->
  <white_list>10.0.0.50</white_list>
  <white_list>192.168.1.0/24</white_list>
  <!-- Increase log output verbosity -->
  <logall>yes</logall>
  <logall_json>yes</logall_json>
</global>

<!-- Rule ignore (flood prevention) - per-rule attribute -->
<!-- <rule id="100001" level="6" ignore="60"> -->
<!-- ignore="60" means don't re-fire within 60 seconds for same source -->

#Threat Hunting: Dashboard KQL

#OpenSearch/Kibana KQL Examples

# ── Authentication ──
rule.groups: "authentication_failure" AND rule.level: [10 TO 15]
data.srcip: "192.168.1.50" AND rule.groups: "authentication_failure"
agent.name: "web01" AND rule.groups: "web_attack"

# ── MITRE ATT&CK ──
rule.mitre.id: "T1059.001"
rule.mitre.tactic: "Lateral Movement"
rule.mitre.id: ("T1003" OR "T1550.002" OR "T1558")

# ── FIM / Syscheck ──
rule.groups: "syscheck" AND syscheck.event: "added" AND syscheck.path: "/etc/*"
rule.groups: "syscheck" AND syscheck.path: "/var/www/html/*" AND syscheck.event: "added"

# ── Vulnerability ──
vulnerability.severity: "critical" AND vulnerability.status: "active"
vulnerability.cve: "CVE-2021-44228"
vulnerability.package.name: "log4j*"

# ── Windows Event IDs ──
data.win.system.eventID: "4625" AND agent.name: "dc01"
data.win.system.eventID: ("4720" OR "4722" OR "4724" OR "4728" OR "4732")

# ── SCA Failures ──
rule.groups: "sca" AND data.sca.result: "failed"
rule.groups: "sca" AND data.sca.policy: "custom_linux_hardening"

# ── High-volume source (potential scanner) ──
# Dashboard > Visualize > Metric by data.srcip, filter rule.level >= 6

# ── Rare process execution (first-time-seen) ──
rule.groups: "syscheck" AND rule.firedtimes: [1 TO 1]

# ── Active Response triggered ──
rule.groups: "active_response"

#Threat Hunting Pivots

Hunt KQL Filter What to look for
Credential dump rule.mitre.id: "T1003" LSASS access, registry SAM
Lateral movement rule.mitre.tactic: "Lateral Movement" New admin logins, psexec
Persistence rule.mitre.tactic: "Persistence" New scheduled tasks, services, users
Defense evasion rule.mitre.tactic: "Defense Evasion" Log clears, AV disables
Exfiltration rule.mitre.tactic: "Exfiltration" Large outbound, DNS tunneling
Webshell syscheck.path: "/var/www/*" AND syscheck.event: "added" New PHP/ASP files in web root

#wazuh-control & Maintenance

#Service Management

# Full service control
/var/ossec/bin/wazuh-control start
/var/ossec/bin/wazuh-control stop
/var/ossec/bin/wazuh-control restart
/var/ossec/bin/wazuh-control reload     # reload rules without restart
/var/ossec/bin/wazuh-control status     # show all daemons

# Individual daemon restart
systemctl restart wazuh-manager         # systemd (preferred)
systemctl restart wazuh-indexer
systemctl restart wazuh-dashboard

# Config syntax test before restart
/var/ossec/bin/wazuh-analysisd -t
/var/ossec/bin/wazuh-remoted -t

# Check for config errors
grep -i "error\|warn" /var/ossec/logs/ossec.log | tail -50

#Useful Maintenance Commands

# Force agent reconnect
/var/ossec/bin/agent_control -R <agent_id>

# Delete agent
/var/ossec/bin/manage_agents -r <agent_id>
# OR via API:
curl -sk -H "Authorization: Bearer $TOKEN" -X DELETE \
  "https://localhost:55000/agents?agents_list=003&status=disconnected"

# Run FIM scan immediately on agent
/var/ossec/bin/agent_control -a -u <agent_id>

# Check cluster status
/var/ossec/bin/cluster_control -l    # list nodes
/var/ossec/bin/cluster_control -i    # node info

# Verify rule fired (logtest)
echo "Jan 10 12:00:01 myhost sshd[1234]: Failed password for root from 10.0.0.1 port 52020 ssh2" \
  | /var/ossec/bin/wazuh-logtest -v

# Stats
/var/ossec/bin/wazuh-analysisd --diag    # analysis engine diag dump

#Also See

#Cyber Aurelien Guidi