Automatische Incident-Analyse mit Wazuh

Rohe Wazuh-Alarme in vollständige Analysen verwandeln - mit einer lokalen KI und Ntfy-Push-Nachrichten.

Automatische Incident-Analyse mit Wazuh

Ein einfacher Sicherheitsalarm ist wie ein nächtlicher Wachmann, der "Da ist ein Geräusch!" ruft – informativ, aber ohne Kontext. Dieser Artikel beschreibt, wie ein Wazuh Active Response Script, angereichert mit einer lokalen KI wie qwen3-coder via Ollama, diesen Wachmann in einen erfahrenen Detektiv verwandelt. Das Ergebnis sind nicht nur Alarme, sondern vollständige, kontextbezogene und priorisierte Fallanalysen, die direkt per Ntfy als Push-Nachricht zugestellt werden.

Die Herausforderung: Von der Alert-Flut zur handlungsrelevanten Einsicht

Jeder, der ein Security Information and Event Management (SIEM) System wie Wazuh betreibt, kennt das Problem: die schiere Menge an Alarmen. Ein isolierter "Login-Fehler"-Alarm kann harmlos sein. Fünfzig solcher Alarme von derselben IP-Adresse innerhalb einer Minute, gefolgt von einem erfolgreichen Login, zeichnen jedoch ein ganz anderes Bild. Die manuelle Korrelation dieser Ereignisse ist zeitaufwändig und fehleranfällig – besonders mitten in der Nacht.

Das Ziel ist es daher, den Prozess der ersten Analyse und Anreicherung zu automatisieren. Der Alarm soll nicht mehr der Startpunkt der Untersuchung sein, sondern bereits eine Zusammenfassung der ersten Untersuchungsschritte enthalten.

Meine Anfänge habe ich bereits hier beschrieben:

SIEM mit KI anreichern
Standard-Wazuh-Alarme in hochpriorisierte, von KI analysierte Vorfallberichte umwandeln.

Inzwischen werden die Logs mit zusätzlichen Informationen angereichert, was ich auch in Form eines kleinen Mini-Scripts zur Verfügung gestellt habe:

Script-Time: Informationen über eine IP
Schnelleinschätzung einer IP-Adresse

Die Architektur

Um diese Aufgabe zu bewältigen, wird eine Kombination aus drei spezialisierten Werkzeugen verwendet, die nahtlos ineinandergreifen.

  1. Wazuh: Das Fundament des Systems. Als Open-Source-SIEM sammelt und analysiert Wazuh Log-Daten von allen überwachten Systemen (Agents). Sein mächtigstes Feature für diese Aufgabe ist die Active Response, die es ermöglicht, bei Auslösung einer bestimmten Regel automatisch Skripte auszuführen.
  2. Ollama & qwen-coder: Das Gehirn der Operation. Ollama ist ein Framework, das es extrem einfach macht, große Sprachmodelle (LLMs) lokal auf der eigenen Hardware zu betreiben. Dies ist aus Sicherheits- und Datenschutzgründen entscheidend, da sensible Log-Daten und Alarme das eigene Netzwerk niemals verlassen.
  3. Ntfy: Der Bote. Ntfy ist ein minimalistischer, selbst-hostbarer Publish-Subscribe-Benachrichtigungsdienst. Er ermöglicht es, Nachrichten per simplem HTTP-Request an ein "Topic" zu senden, welches dann auf Endgeräten wie Smartphones abonniert werden kann.
Ist ein kostenloses SIEM besser?
Wazuh vs. Splunk: SIEM-Vergleich
Private KI ohne Internet
Unglaublich! ChatGPT lokal auf dem Rechner mit Ollama. Anreicherung mit persönlichen Dokumenten mittels PrivateGPT in Docker
Eigener Push-Notification-Service
Selbstgehostete Push-Benachrichtigungen mit ntfy.

Ein Ereignis löst eine Wazuh-Regel aus, die wiederum das Active Response Skript startet. Das Skript sammelt Kontext, schickt alles an die lokale Ollama-API und leitet die von der KI generierte Analyse an den Ntfy-Server weiter, der sie an den Administrator pusht.

Das Skript: Wazuh Active Response im Detail

Das Herzstück meiner Lösung ist ein Bash-Skript, das als Active Response in Wazuh eingebunden wird. Es empfängt den auslösenden Alarm, sammelt zusätzliche Informationen und orchestriert die Kommunikation mit der KI und dem Benachrichtigungsdienst.

#!/bin/bash

# ============================================================================
# Wazuh to Ntfy - Active Response Script
# M. Meister
# Version: 3.1 - Professionelle KI-Analyse mit IP-basiertem Kontext.
# ============================================================================

# --- KONFIGURATION ---
# Hier werden die Zugangsdaten und Endpunkte für die Dienste definiert.
# Sicherstellen, dass diese Werte zur eigenen Umgebung passen.

# Ntfy-Einstellungen
NTFY_USER="DEIN_SEHR_SICHERER_USER"
NTFY_PASS="DEIN_SEHR_SICHERES_PASSWORT"
NTFY_TOPIC_URL="https://push.deine-domain.de/siem"

# KI-Einstellungen
AI_ENABLED=true
OLLAMA_URL="http://ai:11434/api/generate" # Läuft Ollama woanders? IP anpassen!
AI_MODEL="qwen3-coder:latest" # Welches Modell soll die Analyse durchführen?

# Kontext-Einstellungen
CONTEXT_SEARCH_WINDOW=5000 # Wie viele der letzten Events sollen nach der IP durchsucht werden?

# --- Logging ---
LOG_FILE="/var/ossec/logs/active-responses.log"

# --- Skript-Logik ---
# Ab hier beginnt die eigentliche Arbeit.
echo "---" >> ${LOG_FILE}
echo "$(date) ntfy.sh (v3.1): Skript gestartet." >> ${LOG_FILE}

# Wazuh übergibt den Alarm via STDIN. Wir fangen ihn hier auf.
# Ein Timeout verhindert, dass das Skript bei Problemen ewig hängt.
ALERT_JSON=$(timeout 2s cat)
echo "$(date) ntfy.sh (v3.1): Leseversuch beendet." >> ${LOG_FILE}

if [ -z "${ALERT_JSON}" ]; then
    echo "$(date) ntfy.sh (v3.1): [ERROR] ALERT_JSON ist leer. Wazuh hat nichts gesendet?" >> ${LOG_FILE} 2>&1
    exit 1
fi

echo "$(date) ntfy.sh (v3.1): Daten erfolgreich gelesen, beginne mit dem Parsen." >> ${LOG_FILE}

# Mit dem wunderbaren Tool 'jq' extrahieren wir die Rosinen aus dem JSON-Kuchen.
RULE_ID=$(/usr/bin/jq -r '.parameters.alert.rule.id' <<< "$ALERT_JSON")
RULE_LEVEL=$(/usr/bin/jq -r '.parameters.alert.rule.level' <<< "$ALERT_JSON")
RULE_DESC=$(/usr/bin/jq -r '.parameters.alert.rule.description' <<< "$ALERT_JSON")
AGENT_NAME=$(/usr/bin/jq -r '.parameters.alert.agent.name' <<< "$ALERT_JSON")
ATTACKER_IP=$(/usr/bin/jq -r '.parameters.alert.data.src_ip // .parameters.alert.data.client_ip' <<< "$ALERT_JSON") # Versuch, src_ip oder client_ip zu finden

# --- [NEU] Block für KI-Anreicherung mit IP-Kontext ---
AI_ANALYSIS=""
if [ "$AI_ENABLED" = "true" ]; then
    echo "$(date) ntfy.sh (v3.1): KI-Anreicherung ist aktiviert." >> ${LOG_FILE}
    CONTEXT_LOGS="Keine weiteren relevanten Aktivitäten für diese IP gefunden."

    # Nur nach Kontext suchen, wenn eine IP-Adresse im initialen Alarm vorhanden ist.
    if [ -n "$ATTACKER_IP" ] && [ "$ATTACKER_IP" != "null" ]; then
        echo "$(date) ntfy.sh (v3.1): Extrahiere IP ${ATTACKER_IP}. Suche nach Kontext in den letzten ${CONTEXT_SEARCH_WINDOW} Events." >> ${LOG_FILE}
        
        # Durchsuche die letzten N Einträge der alerts.json nach der Angreifer-IP.
        # Das ist der entscheidende Schritt, um der KI die Historie zu liefern.
        RELATED_EVENTS=$(tail -n ${CONTEXT_SEARCH_WINDOW} /var/ossec/logs/alerts/alerts.json | \
                         /usr/bin/jq -r --arg ip "$ATTACKER_IP" 'select(.data.src_ip == $ip or .data.client_ip == $ip) | "- \(.rule.description) (Level \(.rule.level)) am \(.timestamp)"')
        
        if [ -n "$RELATED_EVENTS" ]; then
            CONTEXT_LOGS=${RELATED_EVENTS}
        fi
    else
        echo "$(date) ntfy.sh (v3.1): Kein src_ip oder client_ip Feld im Alarm gefunden. Kontextsuche übersprungen." >> ${LOG_FILE}
    fi

    # Erstelle den Prompt für die KI. Die Qualität des Prompts bestimmt die Qualität der Antwort.
    # Wir geben der KI eine Rolle, den Kontext und eine klare Struktur für die Antwort vor.
    AI_PROMPT=$(cat <<EOF
Du bist ein erfahrener IT-Sicherheitsanalyst. Analysiere den folgenden Vorfall, beziehe dabei die gesamte bekannte Historie der beteiligten IP-Adresse mit ein und formuliere eine prägnante Risikobewertung sowie konkrete Handlungsempfehlungen auf Deutsch in Markdown.

**Primärer Alarm (Auslöser):**
Regel: ${RULE_DESC} (Level ${RULE_LEVEL})
IP-Adresse: ${ATTACKER_IP:-Nicht verfügbar}
Agent: ${AGENT_NAME}

**Bekannte vergangene Aktivitäten der IP-Adresse ${ATTACKER_IP}:**
${CONTEXT_LOGS}

Deine Analyse:
Titel: (Finde einen Aussagekräftigen Titel für den Incident)
Beteiligte Komponenten: (Gib eine Tabelle der beteiligten Systeme und deren Rolle aus.)
Was ist passiert? (Kurze, verständliche Zusammenfassung des Vorfalls im Gesamtkontext.)
Was wind die Indicators of Compromise?
Was ist über den Angreifer im Internet ermittelbar?
Risikobewertung: (Kritisch, Hoch, Mittel, Niedrig - mit kurzer Begründung.)
Empfohlene nächste Schritte: (Konkrete, umsetzbare Anweisungen für den Admin, z.B. "IP an der Firewall blockieren", "Logs auf System X prüfen"...)
EOF
)
    echo "$(date) ntfy.sh (v3.1): Sende Prompt an die KI..." >> ${LOG_FILE}

    # Rufe die Ollama-API auf. Das ist der Moment, in dem die Magie passiert.
    JSON_PAYLOAD=$(jq -n --arg prompt "$AI_PROMPT" --arg model "$AI_MODEL" '{model: $model, prompt: $prompt, stream: false}')
    AI_RESPONSE=$(curl --silent --show-error --max-time 30 -X POST -H "Content-Type: application/json" -d "$JSON_PAYLOAD" "${OLLAMA_URL}")

    # Extrahiere die reine Textantwort aus der JSON-Antwort der API.
    if [ -n "$AI_RESPONSE" ] && [[ $(echo "$AI_RESPONSE" | jq -e '.error' >/dev/null; echo $?) -ne 0 ]]; then
        AI_ANALYSIS=$(echo "$AI_RESPONSE" | /usr/bin/jq -r '.response')
        echo "$(date) ntfy.sh (v3.1): KI-Antwort erfolgreich erhalten." >> ${LOG_FILE}
    else
        echo "$(date) ntfy.sh (v3.1): [WARN] Keine Antwort von der KI erhalten oder API-Fehler: $(echo "$AI_RESPONSE" | jq -r '.error')" >> ${LOG_FILE}
        AI_ANALYSIS="KI-Analyse fehlgeschlagen (Timeout oder API-Fehler)."
    fi
fi
# --- Ende des KI-Blocks ---

# --- Nachricht formatieren ---
# Jetzt bauen wir die finale Nachricht für Ntfy zusammen.
TITLE="${AGENT_NAME}: ${RULE_DESC}"

# Je nach Alarm-Level setzen wir Priorität und Icon für die Benachrichtigung.
if [ "$RULE_LEVEL" -ge 11 ]; then
    PRIORITY="max"
    TAG="rotating_light"
elif [ "$RULE_LEVEL" -ge 7 ]; then
    PRIORITY="high"
    TAG="warning"
else
    PRIORITY="default"
    TAG="loudspeaker"
fi

# Der "Body" der Nachricht, der die KI-Analyse enthält.
MESSAGE_BODY=$(cat <<EOF
**Level:** ${RULE_LEVEL}
**Regel:** ${RULE_ID} - ${RULE_DESC}
**Agent:** ${AGENT_NAME}
**Source IP:** ${ATTACKER_IP:-N/A}

---
### KI-Analyse
${AI_ANALYSIS:-Keine Analyse durchgeführt.}
EOF
)

echo "$(date) ntfy.sh (v3.1): Sende finale Benachrichtigung." >> ${LOG_FILE}

# Sende die finale, angereicherte Benachrichtigung an den Ntfy-Server.
(curl --silent --show-error -u "${NTFY_USER}":"${NTFY_PASS}" \
     -H "Title: ${TITLE}" \
     -H "Priority: ${PRIORITY}" \
     -H "Tag: ${TAG}" \
     -d "${MESSAGE_BODY}" \
     "${NTFY_TOPIC_URL}") >> ${LOG_FILE} 2>&1

echo "$(date) ntfy.sh (v3.1): Skript beendet." >> ${LOG_FILE}
exit 0

Die Einbindung in Wazuh

Damit Wazuh das Skript auch ausführt, muss es in der zentralen Konfigurationsdatei des Wazuh-Managers (/var/ossec/etc/ossec.conf) deklariert und an die gewünschten Regeln gebunden werden.

  1. Skript platzieren: Das obige Skript muss auf dem Wazuh-Manager-Server im Verzeichnis /var/ossec/active-response/bin/ abgelegt und ausführbar gemacht werden (chmod +x /var/ossec/active-response/bin/ntfy.sh).
  2. ossec.conf anpassen: Der folgende Block muss zur Konfigurationsdatei hinzugefügt werden.
<ossec_config>
  <command>
    <name>ntfy-ki-analyse</name>
    <executable>ntfy.sh</executable>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <active-response>
    <command>ntfy-ki-analyse</command>
    <location>local</location>
    <!-- Wir wollen Alarme ab Level 7 analysiert haben -->
    <level>7</level> 
  </active-response>
</ossec_config>

Nach einem Neustart des Wazuh-Managers (systemctl restart wazuh-manager) ist das System scharf. Jeder Alarm mit einem Level von 7 oder höher löst nun das Skript aus. Das ist für den Anfang natürlich noch zu laut. Ein höherer Wert ist sicherlich sinnvoller.

Ein KI-generierter Vorfallsbericht

Was passiert nun, wenn ein Angriffsversuch erkannt wird? Sagen wir, wir möchten wissen, wie das System auf eine erfolgreiche Brute-Force-Attacke reagiert.

Statt einer einfachen Meldung wie login after brute-force erhält man nun die folgende, von der KI generierte und auf sein Smartphone gepushte Analyse:


Erfolgreicher Brute-Force-Angriff auf Webserver mit anschließender Kompromittierung

Beteiligte Komponenten:

Komponente IP-Adresse Rolle im Vorfall
webdocker 192.168.80.43 Kompromittiertes System (Webserver)
Angreifer 128.94.22.129 Quelle des Angriffs
mastergate 192.168.110.1 pf-log Quelle (Firewall)
wazuh-manager N/A Security Information and Event Management (SIEM)

1. Was ist passiert?

Am 29. September 2025 um 23:11:01 UTC begann ein Angreifer von der IP-Adresse 128.94.22.129 einen Brute-Force-Angriff auf den Login-Endpunkt des Webservers webdocker. Die ersten Authentifizierungsversuche für den Benutzer "michael" schlugen fehl und lösten um 23:11:15 UTC einen Alarm wegen mehrfacher fehlgeschlagener Anmeldungen aus. Daraufhin wurde die angreifende IP-Adresse durch eine automatisierte Reaktion für 180 Sekunden blockiert. Trotz dieser Maßnahme setzte der Angreifer seine Versuche fort und schaffte es um 23:14:15 UTC, also kurz nach Ablauf der temporären Sperre, sich erfolgreich anzumelden. Dies führte zur Auslösung eines kritischen Alarms der Stufe 12, der eine erfolgreiche Kompromittierung des Benutzerkontos "michael" bestätigt.

2. Was sind die Indicators of Compromise (IoCs)?

  • Angreifer-IP: 128.94.22.129
  • Kompromittiertes Benutzerkonto: michael
  • Angriffszeitraum: 29.09.2025, 23:11:01 UTC - 23:14:15 UTC
  • Ziel-URL: /login
  • Angreifer User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36

3. Was ist über den Angreifer im Internet ermittelbar?

Die IP-Adresse 128.94.22.129 ist der Microsoft Corporation zugeordnet, mit einem GeoIP-Standort in Frankfurt, Deutschland, was auf die Nutzung von Cloud-Infrastruktur hindeutet. Obwohl Tools wie AbuseIPDB und VirusTotal keine negative Reputation zeigen, ist die IP-Adresse auf sechs DNS-Blacklists (u.a. Spamhaus, Spamcop) gelistet. Dies ist ein starker Indikator dafür, dass die IP bereits in der Vergangenheit für missbräuchliche Aktivitäten wie Spam oder andere Angriffe genutzt wurde. Die Gesamtbewertung aus den externen Quellen deutet auf ein mittleres Risiko hin, wobei die Blacklist-Einträge die größte Warnung darstellen.

4. Risikobewertung:

Kritisch

Begründung: Der erfolgreiche Login nach einem Brute-Force-Angriff stellt eine bestätigte Kompromittierung dar. Der Angreifer hat nun aktiven Zugriff auf das System mit den Rechten des Benutzers "michael". Dies birgt ein unmittelbares und hohes Risiko für Datendiebstahl, Systemmanipulation, Installation von Malware oder die weitere Ausbreitung des Angriffs im internen Netzwerk (Lateral Movement). Die Tatsache, dass die automatisierte temporäre Blockade umgangen wurde, zeigt zudem eine unzureichende Konfiguration der Abwehrmaßnahmen.

5. Empfohlene nächste Schritte:

  1. Sofortmaßnahmen zur Eindämmung:

    • Permanente IP-Sperre: Blockieren Sie die IP-Adresse 128.94.22.129 umgehend und dauerhaft auf der Netzwerk-Firewall für jeglichen ein- und ausgehenden Verkehr.
    • Konto deaktivieren: Deaktivieren Sie das Benutzerkonto "michael" sofort, um weiteren unbefugten Zugriff zu unterbinden.
  2. Untersuchung und Bereinigung:

    • Log-Analyse: Analysieren Sie die Nginx-Access-Logs und Systemprotokolle auf dem Agent webdocker ab dem Zeitpunkt des erfolgreichen Logins (29.09.2025, 23:14:15 UTC), um alle Aktionen des Angreifers zu identifizieren.
    • Systemprüfung: Überprüfen Sie das System auf verdächtige Prozesse, neu erstellte oder modifizierte Dateien, unbekannte Netzwerkverbindungen und die Erstellung neuer Benutzerkonten.
  3. Wiederherstellung und Härtung:

    • Passwort-Reset: Setzen Sie das Passwort des Kontos "michael" zurück, bevor es reaktiviert wird. Stellen Sie sicher, dass ein langes, komplexes und einzigartiges Passwort verwendet wird.
    • Zwei-Faktor-Authentifizierung (2FA): Aktivieren Sie 2FA für das Konto "michael" und alle anderen Konten, insbesondere solche mit administrativen Rechten.
    • Konfiguration der Abwehrmaßnahmen verbessern:
      • Verlängern Sie die Dauer der automatischen IP-Sperre nach Brute-Force-Angriffen erheblich (z. B. auf 24 Stunden oder permanent).
      • Implementieren Sie eine Richtlinie zur Kontosperrung, die Benutzerkonten nach einer bestimmten Anzahl fehlgeschlagener Anmeldeversuche sperrt.
    • Passwortrichtlinien verschärfen: Erzwingen Sie für alle Benutzer die Verwendung komplexer Passwörter.
  4. Umfeldanalyse:

    • Überprüfen Sie die Logs der umliegenden Systeme auf verdächtige Aktivitäten, die von der IP 128.94.22.129 oder vom kompromittierten System webdocker ausgehen, um eine eventuelle Ausbreitung des Angriffs frühzeitig zu erkennen.

Diesen Alarm habe ich natürlich selbst herbeigeführt, um die Ausgabe der KI auf die Probe zu stellen. Ich bin mit dem Ergebnis vorerst recht zufrieden. Ein anderer realer Alarm sah auf dem Smartphone so aus:

Und parallel dazu, erhält man die Nachricht auch auf der SmartWatch:

Eine spätere Analyse dazu zeigte jedoch auf, dass dieser Versuch per UDP stattgefunden hat. Es gab keinerlei Netzwerkverkehr zwischen den beiden IP-Adressen. Die Pakete waren nur am eingehenden Interface zu sehen, da UDP verwendet wurde und wurden nicht an die interne IP weitergeleitet. Diese hat dann auch logischerweise die Malware nicht nachgeladen und ausgeführt.

Fazit

Durch die Kombination von Wazuh, einem lokalen LLM via Ollama und Ntfy wird die Reaktionszeit auf Sicherheitsvorfälle drastisch verkürzt. Anstelle von rohen, kontextlosen Alarmen erhalten Administratoren eine voranalysierte, priorisierte und mit konkreten Handlungsempfehlungen versehene Zusammenfassung. Dieser Automatisierungsgrad hebt das eigene Security Operations Center (SOC) auf ein neues Level und ermöglicht es, sich auf die tatsächliche Behebung von Bedrohungen zu konzentrieren, anstatt auf die mühsame Analyse. Ich glaube, das Projekt bewegt sich in die richtige Richtung. In meinen nächsten Schritten überlege ich, ob ich den KI-Alarm nur durch wichtigere Use-Cases triggere, oder ob die KI aus dem Rauschen die Incidents erkennen soll. Auf jeden Fall kann der Analyse Prompt verbessert werden. Doch ich glaube, über all das wird meine Stromrechnung und die Kühlung entscheiden... 🤔