Skip to main content
DFIRLab
Research
Intel BriefingsThreat Actors
IOC CheckFile AnalyzerPhishing CheckDomain LookupExposure ScannerPrivacy Check
WikiAbout
PlatformNew
DFIRLab

Security research, threat intelligence, and free DFIR tools.

Tools

Phishing CheckerExposure ScannerDomain LookupFile AnalyzerPrivacy Check

Resources

DFIR WikiIntel BriefingsAboutPlatformAPI Docs

Legal

Privacy PolicyRSS FeedSitemap

© 2026 DFIR Lab. All rights reserved.


← Back to Research
WazuhThreat IntelligenceAlert EnrichmentSIEMXDRIntegrationsoc

DFIR Platform + Wazuh: Real-Time Alert Enrichment

DFIR Lab/April 13, 2026/10 min read

Wazuh generates alerts. Thousands of them. A firewall rule triggers on an outbound connection to an unfamiliar IP. An endpoint agent detects a suspicious process spawning PowerShell. A file integrity monitoring rule flags a modified binary. Each alert contains raw observables — IPs, domains, hashes — but no external context about whether those indicators are actually malicious or merely unfamiliar.

This is the gap that Wazuh threat intelligence integration is designed to fill. By connecting Wazuh to an external enrichment source, alerts arrive with reputation data, abuse history, and risk scores already attached. Analysts see context instead of raw data, and triage decisions that previously required manual lookups happen in seconds.

This guide covers integrating DFIR Platform's multi-source IOC enrichment API with Wazuh using two approaches: the Wazuh integratord module for lightweight webhook-style enrichment, and a custom active response script for on-demand enrichment triggered by specific alert rules.


Why Alert Enrichment Matters for Wazuh Deployments

Wazuh excels at detection. Its rule engine processes logs from endpoints, firewalls, cloud services, and applications, matching patterns against a comprehensive rule set. But detection and triage are different problems.

A Wazuh alert telling you that 192.0.2.47 triggered an outbound connection rule is a starting point. Without enrichment, the analyst must manually check whether that IP is associated with known threat actors, whether it has been reported for abuse, what services it exposes, and whether it appears in any threat intelligence feeds. That manual process takes 3-10 minutes per alert — time that compounds quickly when alert volumes reach hundreds per day.

With Wazuh threat intelligence integration via DFIR Platform, the enrichment happens automatically. The alert is augmented with data from 14+ sources before it reaches the analyst's screen. An IP flagged by 9 out of 14 sources with a risk score of 87 gets immediate attention. An IP flagged by zero sources with a risk score of 3 gets deprioritized. The analyst's judgment is applied to ambiguous cases, not to every alert.


Architecture Overview

The integration sits between Wazuh's alert pipeline and the DFIR Platform API:

[Wazuh Agent]
    |
    v
[Wazuh Manager / Alert Engine]
    |
    v
[Rule Match Triggers Integration]
    |
    +--> [integratord] --> [DFIR Platform API] --> [Enriched Alert in Wazuh Dashboard]
    |
    +--> [Active Response] --> [DFIR Platform API] --> [Enriched Data in Active Response Log]

Both approaches call the same DFIR Platform API endpoint (api.dfir-lab.ch/v1/ioc/enrich) and return the same normalized, multi-source enrichment data. The difference is when and how the call is triggered.


Prerequisites

  • Wazuh Manager 4.x installed and running (single-node or cluster)
  • Python 3.8+ on the Wazuh Manager server
  • DFIR Platform API key — sign up at platform.dfir-lab.ch (free tier: 100 credits/month, no credit card required)
  • The requests Python library installed: pip3 install requests
  • Network connectivity from the Wazuh Manager to api.dfir-lab.ch on port 443

Getting Started

Create a free account at platform.dfir-lab.ch and generate an API key from the dashboard. The free tier provides 100 credits per month — enough for approximately 20-33 enrichment lookups to validate the integration. For production alert volumes, the Starter plan ($29/mo for 500 credits) or Professional plan ($79/mo for 2,500 credits) will be more appropriate.


Approach 1: Wazuh Integratord

Wazuh's integratord daemon monitors alerts and forwards matching ones to external scripts. This is the simplest path to enrichment — you write a Python script that receives alert data, calls the DFIR Platform API, and logs the enriched result.

Integration Script

Create the integration script at /var/ossec/integrations/dfir-platform:

python
1#!/usr/bin/env python3
2"""Wazuh integration script for DFIR Platform IOC enrichment."""
3 
4import json
5import sys
6import os
7import requests
8from datetime import datetime
9 
10DFIR_API_KEY = os.environ.get(
11 "DFIR_API_KEY", "your-dfir-platform-api-key"
12)
13DFIR_API_URL = "https://api.dfir-lab.ch/v1"
14LOG_FILE = "/var/ossec/logs/dfir-platform-enrichment.log"
15 
16 
17def log_message(message: str):
18 """Write a timestamped message to the enrichment log."""
19 timestamp = datetime.utcnow().isoformat()
20 with open(LOG_FILE, "a") as f:
21 f.write(f"{timestamp} - {message}\n")
22 
23 
24def enrich_ioc(ioc_type: str, value: str) -> dict:
25 """Call DFIR Platform enrichment API."""
26 headers = {
27 "X-API-Key": DFIR_API_KEY,
28 "Content-Type": "application/json",
29 }
30 try:
31 response = requests.post(
32 f"{DFIR_API_URL}/ioc/enrich",
33 headers=headers,
34 json={"type": ioc_type, "value": value},
35 timeout=30,
36 )
37 response.raise_for_status()
38 return response.json()
39 except requests.exceptions.RequestException as e:
40 log_message(f"API error for {ioc_type}:{value} - {str(e)}")
41 return {}
42 
43 
44def extract_observables(alert: dict) -> list:
45 """Extract enrichable observables from a Wazuh alert."""
46 observables = []
47 data = alert.get("data", {})
48 
49 # Source IP from network alerts
50 src_ip = data.get("srcip") or alert.get("data", {}).get("src_ip")
51 if src_ip and not src_ip.startswith(("10.", "172.16.", "192.168.", "127.")):
52 observables.append(("ip", src_ip))
53 
54 # Destination IP
55 dst_ip = data.get("dstip") or data.get("dst_ip")
56 if dst_ip and not dst_ip.startswith(("10.", "172.16.", "192.168.", "127.")):
57 observables.append(("ip", dst_ip))
58 
59 # Domain from DNS queries or URL fields
60 domain = data.get("hostname") or data.get("domain")
61 if domain:
62 observables.append(("domain", domain))
63 
64 # File hash from FIM or syscheck
65 md5 = data.get("md5") or alert.get("syscheck", {}).get("md5_after")
66 sha256 = data.get("sha256") or alert.get("syscheck", {}).get("sha256_after")
67 if sha256:
68 observables.append(("hash", sha256))
69 elif md5:
70 observables.append(("hash", md5))
71 
72 return observables
73 
74 
75def main():
76 """Process incoming Wazuh alert and enrich observables."""
77 # integratord passes alert file path as first argument
78 alert_file = sys.argv[1]
79 
80 with open(alert_file, "r") as f:
81 alert = json.load(f)
82 
83 rule_id = alert.get("rule", {}).get("id", "unknown")
84 rule_desc = alert.get("rule", {}).get("description", "unknown")
85 
86 observables = extract_observables(alert)
87 if not observables:
88 log_message(f"No enrichable observables in rule {rule_id}")
89 return
90 
91 for ioc_type, value in observables:
92 log_message(f"Enriching {ioc_type}:{value} from rule {rule_id}")
93 result = enrich_ioc(ioc_type, value)
94 
95 if result:
96 risk_score = result.get("risk_score", 0)
97 verdict = result.get("verdict", "unknown")
98 sources_flagged = result.get("sources_flagged", 0)
99 sources_queried = result.get("sources_queried", 0)
100 
101 log_message(
102 f"Result for {ioc_type}:{value} - "
103 f"score:{risk_score} verdict:{verdict} "
104 f"flagged:{sources_flagged}/{sources_queried}"
105 )
106 
107 # Write enrichment data as a Wazuh alert for dashboard visibility
108 enriched_alert = {
109 "integration": "dfir-platform",
110 "dfir_platform": {
111 "ioc_type": ioc_type,
112 "ioc_value": value,
113 "risk_score": risk_score,
114 "verdict": verdict,
115 "sources_queried": sources_queried,
116 "sources_flagged": sources_flagged,
117 "original_rule_id": rule_id,
118 "original_rule_description": rule_desc,
119 },
120 }
121 print(json.dumps(enriched_alert))
122 
123 
124if __name__ == "__main__":
125 main()

Set the script as executable:

bash
1chmod 750 /var/ossec/integrations/dfir-platform
2chown root:wazuh /var/ossec/integrations/dfir-platform

Wazuh Manager Configuration

Add the integration block to /var/ossec/etc/ossec.conf:

xml
1<integration>
2 <name>dfir-platform</name>
3 <level>7</level>
4 <alert_format>json</alert_format>
5</integration>

The <level>7</level> filter ensures only alerts at level 7 or above trigger enrichment. Adjust this threshold based on your alert volume and credit budget. Higher thresholds mean fewer enrichment calls (and fewer credits consumed) but may miss moderate-severity indicators.

To restrict enrichment to specific rule groups — for example, only network-related alerts — use the <group> filter:

xml
1<integration>
2 <name>dfir-platform</name>
3 <group>network,ids,firewall</group>
4 <alert_format>json</alert_format>
5</integration>

Restart the Wazuh Manager to apply:

bash
1systemctl restart wazuh-manager

Approach 2: Active Response Script

For cases where you want enrichment triggered by specific rules rather than broad alert categories, Wazuh's active response framework provides more granular control.

Active Response Script

Create the script at /var/ossec/active-response/bin/dfir-enrich.py:

python
1#!/usr/bin/env python3
2"""Wazuh active response script for DFIR Platform enrichment."""
3 
4import json
5import sys
6import os
7import requests
8 
9DFIR_API_KEY = os.environ.get(
10 "DFIR_API_KEY", "your-dfir-platform-api-key"
11)
12DFIR_API_URL = "https://api.dfir-lab.ch/v1"
13OUTPUT_LOG = "/var/ossec/logs/active-responses.log"
14 
15 
16def main():
17 # Read the alert from stdin (active response protocol)
18 input_data = sys.stdin.read()
19 try:
20 alert = json.loads(input_data)
21 except json.JSONDecodeError:
22 return
23 
24 # Extract the source IP from the alert
25 src_ip = (
26 alert.get("parameters", {}).get("alert", {})
27 .get("data", {}).get("srcip")
28 )
29 if not src_ip:
30 return
31 
32 # Skip private IPs
33 if src_ip.startswith(("10.", "172.16.", "192.168.", "127.")):
34 return
35 
36 headers = {
37 "X-API-Key": DFIR_API_KEY,
38 "Content-Type": "application/json",
39 }
40 
41 try:
42 response = requests.post(
43 f"{DFIR_API_URL}/ioc/enrich",
44 headers=headers,
45 json={"type": "ip", "value": src_ip},
46 timeout=30,
47 )
48 response.raise_for_status()
49 result = response.json()
50 
51 risk_score = result.get("risk_score", 0)
52 log_entry = {
53 "active_response": "dfir-enrich",
54 "ip": src_ip,
55 "risk_score": risk_score,
56 "verdict": result.get("verdict", "unknown"),
57 "sources_flagged": result.get("sources_flagged", 0),
58 }
59 
60 with open(OUTPUT_LOG, "a") as f:
61 f.write(json.dumps(log_entry) + "\n")
62 
63 except requests.exceptions.RequestException:
64 pass
65 
66 
67if __name__ == "__main__":
68 main()

Active Response Configuration

Add to /var/ossec/etc/ossec.conf:

xml
1<command>
2 <name>dfir-enrich</name>
3 <executable>dfir-enrich.py</executable>
4 <timeout_allowed>no</timeout_allowed>
5</command>
6 
7<active-response>
8 <command>dfir-enrich</command>
9 <location>server</location>
10 <rules_id>5710,5712,31101,31104</rules_id>
11</active-response>

The <rules_id> field specifies which Wazuh rules trigger the enrichment. In this example:

  • 5710, 5712 — SSH authentication failure rules (brute-force attempts)
  • 31101, 31104 — Web server attack detection rules

Customize this list to match the rules that generate alerts with external IPs relevant to your environment.


Wazuh Threat Intelligence Integration: Custom Rules for Enriched Data

To make enrichment results visible in the Wazuh dashboard, create custom rules that parse the enrichment output. Add to /var/ossec/etc/rules/local_rules.xml:

xml
1<group name="dfir-platform,">
2 <rule id="100100" level="3">
3 <decoded_as>json</decoded_as>
4 <field name="integration">dfir-platform</field>
5 <description>DFIR Platform enrichment result received</description>
6 </rule>
7 
8 <rule id="100101" level="10" frequency="1">
9 <if_sid>100100</if_sid>
10 <field name="dfir_platform.risk_score">^[7-9][0-9]$|^100$</field>
11 <description>DFIR Platform: High-risk IOC detected (score >= 70)</description>
12 </rule>
13 
14 <rule id="100102" level="7" frequency="1">
15 <if_sid>100100</if_sid>
16 <field name="dfir_platform.risk_score">^[4-6][0-9]$</field>
17 <description>DFIR Platform: Suspicious IOC detected (score 40-69)</description>
18 </rule>
19 
20 <rule id="100103" level="3" frequency="1">
21 <if_sid>100100</if_sid>
22 <field name="dfir_platform.risk_score">^[0-3]?[0-9]$</field>
23 <description>DFIR Platform: Low-risk IOC (score < 40)</description>
24 </rule>
25</group>

These rules create tiered alert levels based on the enrichment risk score. High-risk IOCs (score 70+) generate level 10 alerts, which appear prominently in the Wazuh dashboard and can trigger further active responses — such as automated firewall blocks or ticket creation.


Example Alert Flow

Here is a concrete end-to-end scenario.

1. Alert generation. A Wazuh agent on a Linux server detects 15 failed SSH login attempts from 203.0.113.47 within 2 minutes. Rule 5712 fires at alert level 10.

2. Integration trigger. The integratord daemon matches the alert against the configured integration (level >= 7) and passes it to the dfir-platform script.

3. Observable extraction. The script extracts 203.0.113.47 as a source IP, confirms it is not a private address, and calls the DFIR Platform API.

4. Multi-source enrichment. The API queries 14+ sources in parallel: AbuseIPDB returns 127 reports in the last 90 days. Three threat intelligence feeds flag the IP as a known brute-force source. Passive DNS shows the IP has hosted multiple domains associated with scanning infrastructure. Geolocation places it in a data center in Eastern Europe.

5. Enriched alert. The script outputs a JSON alert with risk_score: 91, verdict: malicious, sources_flagged: 11. Wazuh ingests this as a new alert, which matches custom rule 100101 (high-risk IOC, level 10).

6. Dashboard visibility. The SOC analyst sees both the original SSH brute-force alert and the enrichment alert in the Wazuh dashboard. The enrichment data confirms the source IP is a known malicious actor. The analyst authorizes a firewall block with confidence, without needing to manually query any external service.


Credit Management and Optimization

DFIR Platform enrichment consumes approximately 3-5 credits per lookup. For a Wazuh deployment generating hundreds of alerts per day, credit management matters.

Filter aggressively. Use the <level>, <group>, and <rules_id> filters to restrict enrichment to alerts that actually contain external indicators worth checking. Internal-only alerts, compliance events, and informational logs do not benefit from IOC enrichment.

Deduplicate at the script level. The integration script can maintain a simple in-memory or file-based cache of recently enriched IOCs. If 203.0.113.47 was enriched 5 minutes ago, skip the API call and reuse the cached result. A 30-60 minute cache TTL balances freshness against credit consumption.

Start with targeted rules. Begin by enabling enrichment for 3-5 high-value rule IDs. Monitor the credit usage in the DFIR Platform dashboard for a week, then expand coverage as you understand the volume.

Upgrade when ready. The free tier (100 credits/month) is sufficient for proof-of-concept. For production Wazuh deployments, the Starter plan at $29/month provides 500 credits, and the Professional plan at $79/month provides 2,500 credits — enough for continuous enrichment across moderate to high alert volumes.


Conclusion

Wazuh detects threats. DFIR Platform contextualizes them. The integration — whether through integratord for broad enrichment or active response for targeted lookups — transforms raw alerts into actionable intelligence by adding multi-source reputation data, risk scores, and abuse history from 14+ sources.

The setup requires one Python script, one configuration block in ossec.conf, and one API key. From there, every qualifying alert arrives with the context an analyst needs to make a fast, confident triage decision.

Sign up for a free account at platform.dfir-lab.ch — 100 credits per month, no credit card required. Use code LAUNCH50 for 50% off your first paid month on Starter or Professional plans.

Table of Contents

  • Why Alert Enrichment Matters for Wazuh Deployments
  • Architecture Overview
  • Prerequisites
  • Getting Started
  • Approach 1: Wazuh Integratord
  • Integration Script
  • Wazuh Manager Configuration
  • Approach 2: Active Response Script
  • Active Response Script
  • Active Response Configuration
  • Wazuh Threat Intelligence Integration: Custom Rules for Enriched Data
  • Example Alert Flow
  • Credit Management and Optimization
  • Conclusion
Share on XShare on LinkedIn
DFIR Platform

Incident Response. Automated.

Analyze phishing emails, enrich IOCs, triage alerts, and generate forensic reports — from your terminal with dfir-cli or through the REST API.

Phishing Analysis

Headers, URLs, attachments + AI verdict

IOC Enrichment

Multiple threat intel providers

Exposure Scanner

Attack surface mapping

CLI & API

Terminal-first, JSON output

Start FreeFree tier · No credit card required

Related Research

SplunkIOC EnrichmentCustom Search Command+3

DFIR Platform + Splunk: IOC Enrichment via Custom Search Commands

Build a Splunk custom search command that enriches IOCs via DFIR Platform API. Includes Python code, commands.conf configuration, packaging as a Splunk app, and example SPL queries.

Apr 14, 202611 min read
oc-enrichmentThreat Intelligencevirustotal+4

VirusTotal API Alternative: Cheaper Multi-Source IOC Enrichment for Security Teams

Apr 15, 20269 min read
TheHiveIOC EnrichmentCortex+3

DFIR Platform + TheHive: Automated IOC Enrichment for Case Management

Integrate DFIR Platform's multi-source IOC enrichment API with TheHive as a Cortex analyzer. Python code examples, architecture walkthrough, and step-by-step setup for SOC teams.

Apr 12, 202611 min read