Wazuh Third-Party Integrations - Osquery, MISP, SOAR

Wazuh 4.14 extends its monitoring and response capabilities through integration with third-party security platforms. The Osquery module provides advanced endpoint telemetry, threat intelligence platforms (MISP, OpenCTI) enable alert enrichment with indicators of compromise, and SOAR solutions automate the full incident response lifecycle.

Osquery Integration

Osquery is a tool for querying operating system state through a SQL-like interface. Wazuh integrates with Osquery via the wodle module, enabling scheduled queries on agents with centralized result analysis on the server.

Configuring the Osquery Wodle

The module is configured in the agent configuration file /var/ossec/etc/ossec.conf:

<wodle name="osquery">
  <disabled>no</disabled>
  <run_daemon>yes</run_daemon>
  <log_path>/var/log/osquery/osqueryd.results.log</log_path>
  <config_path>/etc/osquery/osquery.conf</config_path>
  <add_labels>yes</add_labels>
</wodle>

Module parameters:

ParameterDescriptionDefault
<disabled>Enable or disable the moduleyes
<run_daemon>Manage the osqueryd daemonyes
<log_path>Path to the Osquery results log/var/log/osquery/osqueryd.results.log
<config_path>Path to the Osquery configuration/etc/osquery/osquery.conf
<add_labels>Append agent labels to resultsyes

Osquery Pack Configuration

Osquery uses JSON configuration files to define scheduled queries:

{
  "schedule": {
    "system_info": {
      "query": "SELECT hostname, cpu_brand, physical_memory FROM system_info;",
      "interval": 3600,
      "description": "System information"
    },
    "open_ports": {
      "query": "SELECT DISTINCT pid, port, protocol, address FROM listening_ports WHERE port != 0;",
      "interval": 300,
      "description": "Open network ports"
    },
    "installed_packages": {
      "query": "SELECT name, version, source FROM deb_packages UNION SELECT name, version, source FROM rpm_packages;",
      "interval": 86400,
      "description": "Installed packages inventory"
    },
    "suspicious_processes": {
      "query": "SELECT p.name, p.path, p.cmdline, p.uid, u.username FROM processes p LEFT JOIN users u ON p.uid = u.uid WHERE p.on_disk = 0;",
      "interval": 60,
      "description": "Processes running from deleted binaries"
    }
  }
}

Detection Rules for Osquery

Wazuh analyzes Osquery results through its rule engine. Built-in rules cover basic scenarios, but custom rules are recommended for advanced monitoring:

<rule id="100200" level="10">
  <decoded_as>osquery</decoded_as>
  <field name="osquery.name">suspicious_processes</field>
  <description>Osquery: process running from deleted binary detected on $(agent.name)</description>
  <mitre>
    <id>T1036</id>
  </mitre>
  <group>osquery,process_anomaly,</group>
</rule>

For more on creating detection rules, see the Wazuh Capabilities section.

Threat Intelligence - MISP

MISP (Malware Information Sharing Platform) is a platform for sharing indicators of compromise (IoC). The Wazuh integration automatically checks observables (IP addresses, domains, file hashes) against the MISP IoC database.

MISP Integration Architecture

Wazuh Alert --> Custom Integration Script --> MISP API --> IoC Lookup
                                                              |
                                                    Match Found? --> Enriched Alert

Configuration

  1. Generate an API key in MISP: Administration - Auth Keys - Add authentication key
  2. Install the integration script:
#!/usr/bin/env python3
"""MISP integration for Wazuh - IoC lookup."""
import sys
import json
import requests
from typing import Optional

MISP_URL = "https://misp.example.com"
MISP_API_KEY = "YOUR_MISP_API_KEY"
VERIFY_SSL = True

def search_ioc(value: str, ioc_type: str) -> Optional[dict]:
    """Search for an IoC in MISP."""
    headers = {
        "Authorization": MISP_API_KEY,
        "Accept": "application/json",
        "Content-Type": "application/json",
    }
    payload = {
        "returnFormat": "json",
        "value": value,
        "type": ioc_type,
    }
    response = requests.post(
        f"{MISP_URL}/attributes/restSearch",
        json=payload,
        headers=headers,
        verify=VERIFY_SSL,
    )
    if response.status_code == 200:
        results = response.json()
        if results.get("response", {}).get("Attribute"):
            return results["response"]["Attribute"][0]
    return None

def main():
    alert_file = sys.argv[1]
    with open(alert_file) as f:
        alert = json.load(f)

    src_ip = alert.get("data", {}).get("srcip")
    if src_ip:
        result = search_ioc(src_ip, "ip-src")
        if result:
            print(json.dumps({
                "misp_match": True,
                "event_id": result.get("event_id"),
                "category": result.get("category"),
                "comment": result.get("comment"),
            }))

if __name__ == "__main__":
    main()
  1. Configure in ossec.conf:
<integration>
  <name>custom-misp</name>
  <hook_url>https://misp.example.com</hook_url>
  <api_key>YOUR_MISP_API_KEY</api_key>
  <level>7</level>
  <group>web,sshd,firewall,</group>
  <alert_format>json</alert_format>
</integration>

IoC Types for Lookup

IoC TypeWazuh Alert SourceMISP Type
Source IPdata.srcipip-src
Destination IPdata.dstipip-dst
Domaindata.hostnamedomain
File hash (MD5)syscheck.md5_aftermd5
File hash (SHA256)syscheck.sha256_aftersha256
URLdata.urlurl

Threat Intelligence - OpenCTI

OpenCTI is a cyber threat intelligence management platform supporting STIX/TAXII standards.

Integration via TAXII Feed

Wazuh can consume IoCs from OpenCTI through a TAXII server:

  1. Configure a collection export in OpenCTI with a TAXII endpoint
  2. Create a periodic synchronization script that converts IoCs to Wazuh CDB list format:
#!/bin/bash
# Sync OpenCTI IoC to Wazuh CDB lists
TAXII_URL="https://opencti.example.com/taxii2/feeds"
OUTPUT_DIR="/var/ossec/etc/lists"

# Download IP indicators
curl -sk -H "Authorization: Bearer ${OPENCTI_TOKEN}" \
  "${TAXII_URL}/ip-indicators" \
  | jq -r '.objects[] | select(.type=="indicator") | .pattern' \
  | sed "s/\[ipv4-addr:value = '//;s/'\]//" \
  | while read ip; do echo "${ip}:malicious"; done \
  > "${OUTPUT_DIR}/opencti-malicious-ips"

# Reload Wazuh rules
/var/ossec/bin/wazuh-control reload
  1. Reference the CDB list in rules:
<rule id="100210" level="12">
  <if_sid>5710</if_sid>
  <list field="srcip" lookup="address_match_key">etc/lists/opencti-malicious-ips</list>
  <description>Connection from IP flagged in OpenCTI threat intelligence: $(srcip)</description>
  <mitre>
    <id>T1071</id>
  </mitre>
  <group>threat_intelligence,opencti,</group>
</rule>

Ticketing Systems - Jira

The Jira integration automatically creates issues when critical alerts fire.

Configuring the Jira Integration

  1. Generate an API Token in Jira: Account Settings - Security - API Tokens
  2. Install the integration script:
#!/usr/bin/env python3
"""Jira integration for Wazuh - automatic ticket creation."""
import sys
import json
import requests
from base64 import b64encode

JIRA_URL = "https://company.atlassian.net"
JIRA_USER = "security-bot@company.com"
JIRA_TOKEN = "YOUR_JIRA_API_TOKEN"
JIRA_PROJECT = "SEC"

def create_ticket(alert: dict) -> dict:
    """Create a Jira issue from a Wazuh alert."""
    rule = alert.get("rule", {})
    agent = alert.get("agent", {})

    level = rule.get("level", 0)
    if level >= 12:
        priority = "Critical"
    elif level >= 10:
        priority = "High"
    elif level >= 7:
        priority = "Medium"
    else:
        priority = "Low"

    issue_data = {
        "fields": {
            "project": {"key": JIRA_PROJECT},
            "summary": f"[Wazuh] {rule.get('description', 'Security Alert')}",
            "description": (
                f"*Alert Details*\n"
                f"- Rule ID: {rule.get('id')}\n"
                f"- Level: {level}\n"
                f"- Agent: {agent.get('name')} ({agent.get('id')})\n"
                f"- Timestamp: {alert.get('timestamp')}\n"
                f"- Full log: {alert.get('full_log', 'N/A')}\n"
            ),
            "issuetype": {"name": "Bug"},
            "priority": {"name": priority},
            "labels": ["wazuh", "security-alert"],
        }
    }

    auth = b64encode(f"{JIRA_USER}:{JIRA_TOKEN}".encode()).decode()
    headers = {
        "Authorization": f"Basic {auth}",
        "Content-Type": "application/json",
    }

    response = requests.post(
        f"{JIRA_URL}/rest/api/3/issue",
        json=issue_data,
        headers=headers,
    )
    return response.json()

def main():
    alert_file = sys.argv[1]
    with open(alert_file) as f:
        alert = json.load(f)
    create_ticket(alert)

if __name__ == "__main__":
    main()
  1. Configure in ossec.conf:
<integration>
  <name>custom-jira</name>
  <hook_url>https://company.atlassian.net</hook_url>
  <api_key>YOUR_JIRA_API_TOKEN</api_key>
  <level>10</level>
  <alert_format>json</alert_format>
</integration>

Ticketing Systems - ServiceNow

ServiceNow integration is implemented through the REST API or webhooks.

REST API Configuration

#!/usr/bin/env python3
"""ServiceNow integration for Wazuh - incident creation."""
import sys
import json
import requests

SNOW_INSTANCE = "company.service-now.com"
SNOW_USER = "wazuh-integration"
SNOW_PASSWORD = "YOUR_PASSWORD"

def create_incident(alert: dict) -> dict:
    """Create a ServiceNow incident from a Wazuh alert."""
    rule = alert.get("rule", {})
    agent = alert.get("agent", {})

    level = rule.get("level", 0)
    urgency_map = {range(12, 16): "1", range(10, 12): "2", range(7, 10): "3"}
    urgency = "3"
    for level_range, urg in urgency_map.items():
        if level in level_range:
            urgency = urg
            break

    incident = {
        "short_description": f"Wazuh Alert: {rule.get('description')}",
        "description": json.dumps(alert, indent=2),
        "urgency": urgency,
        "category": "Security",
        "subcategory": "Threat Detection",
        "assignment_group": "Security Operations",
        "caller_id": "wazuh-integration",
    }

    response = requests.post(
        f"https://{SNOW_INSTANCE}/api/now/table/incident",
        json=incident,
        auth=(SNOW_USER, SNOW_PASSWORD),
        headers={"Content-Type": "application/json", "Accept": "application/json"},
    )
    return response.json()

def main():
    alert_file = sys.argv[1]
    with open(alert_file) as f:
        alert = json.load(f)
    create_incident(alert)

if __name__ == "__main__":
    main()

Alternative: ServiceNow Webhook

ServiceNow supports Inbound REST API, enabling the built-in webhook mechanism:

<integration>
  <name>custom-servicenow</name>
  <hook_url>https://company.service-now.com/api/now/table/incident</hook_url>
  <api_key>BASE64_ENCODED_CREDENTIALS</api_key>
  <level>10</level>
  <alert_format>json</alert_format>
</integration>

SOAR - Shuffle

Shuffle provides a visual workflow builder for automating incident response.

Advanced Shuffle Configuration

Beyond the basic webhook integration described in SIEM Integrations , Shuffle supports bidirectional communication with Wazuh through the API.

Example Workflow: IP Blocking via Active Response

  1. Trigger: webhook receives an alert from Wazuh
  2. Condition: check alert level >= 10 and srcip presence
  3. Enrichment: query IP reputation via AbuseIPDB
  4. Decision: if IP is in the blocklist, proceed
  5. Action: call the Wazuh API to trigger Active Response:
{
  "command": "firewall-drop0",
  "arguments": ["-", "srcip", "${srcip}"],
  "alert": {
    "data": {
      "srcip": "${srcip}"
    }
  }
}
  1. Notification: send the result to a Slack channel

Monitoring Shuffle Health

To monitor alert delivery to Shuffle, configure a health-check rule:

<rule id="100220" level="3">
  <decoded_as>json</decoded_as>
  <field name="integration">shuffle</field>
  <description>Shuffle integration: alert forwarded successfully</description>
  <group>integration_health,</group>
</rule>

SOAR - Cortex XSOAR

Cortex XSOAR (formerly Demisto) is a commercial SOAR platform by Palo Alto Networks.

Integration via Syslog

Cortex XSOAR accepts data over syslog. Configure syslog forwarding in Wazuh:

<syslog_output>
  <server>xsoar.example.com</server>
  <port>1514</port>
  <format>json</format>
  <level>7</level>
</syslog_output>

Integration via REST API

Cortex XSOAR provides a REST API for incident creation:

#!/usr/bin/env python3
"""Cortex XSOAR integration for Wazuh."""
import sys
import json
import requests

XSOAR_URL = "https://xsoar.example.com"
XSOAR_API_KEY = "YOUR_XSOAR_API_KEY"

def create_incident(alert: dict) -> dict:
    """Create a Cortex XSOAR incident."""
    rule = alert.get("rule", {})
    severity_map = {
        range(0, 4): 0.5,   # Info
        range(4, 7): 1,     # Low
        range(7, 10): 2,    # Medium
        range(10, 12): 3,   # High
        range(12, 16): 4,   # Critical
    }

    level = rule.get("level", 0)
    severity = 1
    for level_range, sev in severity_map.items():
        if level in level_range:
            severity = sev
            break

    incident = {
        "name": f"Wazuh: {rule.get('description')}",
        "type": "Wazuh Alert",
        "severity": severity,
        "details": json.dumps(alert),
        "labels": [
            {"type": "rule_id", "value": str(rule.get("id"))},
            {"type": "agent", "value": alert.get("agent", {}).get("name", "")},
        ],
    }

    headers = {
        "Authorization": XSOAR_API_KEY,
        "Content-Type": "application/json",
        "Accept": "application/json",
    }

    response = requests.post(
        f"{XSOAR_URL}/incident",
        json=incident,
        headers=headers,
    )
    return response.json()

def main():
    alert_file = sys.argv[1]
    with open(alert_file) as f:
        alert = json.load(f)
    create_incident(alert)

if __name__ == "__main__":
    main()

Playbooks for Wazuh

Cortex XSOAR supports playbooks - automated response workflows. Recommended playbooks for Wazuh alerts:

PlaybookTriggerActions
Brute Force ResponseRule group: authentication_failedBlock IP, notify SOC, create ticket
Malware TriageRule group: syscheck + VirusTotal matchIsolate host, collect artifacts, analyze
Vulnerability ManagementRule group: vulnerability-detectorPrioritize CVE, assign owner

Troubleshooting

Osquery Returns No Results

  1. Check the Osquery daemon status:
systemctl status osqueryd
  1. Verify the <log_path> in the wodle matches the actual log location:
ls -la /var/log/osquery/osqueryd.results.log
  1. Validate the Osquery configuration:
osqueryi --config_path /etc/osquery/osquery.conf --config_check

MISP Integration Errors

  • SSL errors: ensure the MISP certificate is trusted on the Wazuh server, or set VERIFY_SSL = False for testing
  • 403 Forbidden: verify the API key permissions - at minimum, auth_key access to attributes is required
  • Timeouts: increase the requests timeout to 30 seconds for large IoC databases

Jira/ServiceNow Tickets Not Created

  1. Verify network connectivity:
curl -sk https://company.atlassian.net/rest/api/3/serverInfo
  1. Confirm the API token has not expired
  2. Check project permissions - the integration user must have permission to create issues

Shuffle Workflow Not Triggering

  1. Review integratord logs:
grep shuffle /var/ossec/logs/integrations.log
  1. Confirm the webhook URL in Shuffle is active (green indicator)
  2. Verify the workflow is published (not in Draft mode)

For guidance on writing custom integration scripts, see the Custom Integration Development section.

Last updated on