Wazuh-Korrelation für Brute-Force über mehrere Systeme: Warum nicht triggert – und wie du es stabil löst


Einleitung

Brute-Force- und Password-Spraying-Angriffe laufen selten „sauber“ auf einem einzigen System. In realen SIEM-Szenarien verteilen sich fehlgeschlagene Anmeldungen über verschiedene Hosts, Dienste und Logquellen – oft mit identischem Quell-IP-Adressraum oder identischem Benutzernamen. Wazuh kann solche Muster grundsätzlich über Frequenz-/Zeitfenster-Regeln korrelieren. In der Praxis scheitern entsprechende Custom Rules jedoch häufig daran, dass die Korrelation nicht auf Gruppenbasis auslöst. Dieser Beitrag erklärt die Ursache, ordnet sie in die Wazuh-Architektur ein und zeigt praxistaugliche Workarounds.


Ausgangslage / Problemstellung

Es existieren bereits Alerts zu „authentication failed“. Diese Events enthalten Felder wie srcip und dstuser (bzw. im Alert-JSON häufig unter data.*). Darauf aufbauend sollen Korrelationen greifen, z. B.:

  • Mehrere fehlgeschlagene Authentifizierungen von derselben IP innerhalb von 120 Sekunden
  • Mehrere fehlgeschlagene Authentifizierungen für denselben User innerhalb von 120 Sekunden

Versuche mit frequency/timeframe und <if_matched_group>authentication_failed</if_matched_group> triggern nicht – obwohl die Basis-Events sichtbar sind und identische Felder wiederholt auftreten. Ersetzt man <if_matched_group> durch <if_matched_sid>, funktioniert es.


Technische Analyse

1) Wie Wazuh-Korrelation technisch arbeitet

Die klassische Rule-Engine (wazuh-analysisd) verarbeitet Events in einer Pipeline:

  1. Decoding: Felder wie srcip, dstuser, user, id werden extrahiert.
  2. Rules: Ein Event kann eine oder mehrere Rules matchen (in der aktuellen Engine jedoch mit Einschränkungen).
  3. Korrelation: Einige Regeln können über frequency/timeframe und „same_*“-Bedingungen wiederkehrende Ereignisse korrelieren.

Wichtig: Die Korrelation ist im aktuellen wazuh-analysisd funktional, aber in der Tiefe limitiert – insbesondere bei komplexen Abhängigkeiten („Regeln auf Gruppen, die wiederum durch andere Regeln gesetzt werden“).

2) Warum <if_matched_group> in diesem Szenario nicht zuverlässig ist

<if_matched_group> klingt wie der naheliegende Ansatz („korreliere alles, was zur Gruppe authentication_failed gehört“). In der Praxis stößt man hier aber auf eine Einschränkung: Die Korrelation über Gruppenzuordnung kann als eine Art „verschachtelte Korrelation“ wirken – das heißt, die Korrelationsregel erwartet einen stabil referenzierbaren Match-Kontext, den analysisd nicht in allen Fällen über Gruppen sauber aufbaut.

Das Ergebnis: Basis-Regeln feuern (und vergeben Gruppen), aber die Korrelationsregel bekommt diese Gruppenzuordnung nicht so „greifbar“, dass sie zuverlässig über frequency/timeframe darauf korrelieren kann.

3) Feldnamen: dstuser vs. data.dstusr vs. data.dstuser

Ein zusätzlicher Stolperstein ist die Benennung von Feldern:

  • In Rules werden typischerweise Decoder-Felder verwendet (srcip, dstuser, user, …).
  • In Alerts/JSON sieht man oft dieselben Werte als data.*-Struktur.

Für <same_field> musst du genau das Feld angeben, das die Rule-Engine intern als Feld kennt. Wenn du im Alert-Output „data.dstusr“ siehst, heißt das nicht automatisch, dass im Rule-Context dstusr existiert. In vielen Integrationen ist es dstuser oder user. Ein falscher Feldname verhindert Korrelation vollständig.


Lösung / Best Practices

Workaround 1: Von <if_matched_group> auf <if_matched_sid> wechseln (stabilster Ansatz)

Statt auf die Gruppe zu zielen, korrelierst du explizit auf die Rule IDs der Basis-Regeln, die die einzelnen „authentication failed“-Events erzeugen.

Beispiel (Schema):

<rule id="555102" level="10" frequency="4" timeframe="120">
  <if_matched_sid>BASE_RULE_ID_1,BASE_RULE_ID_2,BASE_RULE_ID_3</if_matched_sid>
  <description>Viele fehlgeschlagene Authentifizierungen von derselben IP</description>
  <same_srcip/>
</rule>

<rule id="555103" level="10" frequency="4" timeframe="120">
  <if_matched_sid>BASE_RULE_ID_1,BASE_RULE_ID_2,BASE_RULE_ID_3</if_matched_sid>
  <description>Viele fehlgeschlagene Authentifizierungen für denselben Benutzer</description>
  <same_field>dstuser</same_field>
</rule>

Wichtig in der Praxis:

  • Trage alle relevanten Basis-Rule-IDs ein (z. B. Windows, Linux, VPN, Web-App Auth, IAM).
  • Erhöhe frequency realistisch (2–3 kann im Rauschen untergehen; 4–10 ist oft sinnvoller).
  • Nutze bei User-Korrelation bevorzugt <same_field>dstuser</same_field> oder <same_user/> nur dann, wenn du sicher bist, welches Feld bei deinen Quellen wirklich befüllt wird.

Workaround 2: Einheitliche „Basisregel“ schaffen und darauf korrelieren

Wenn deine „authentication failed“-Events aus verschiedenen Quellen stammen und sehr unterschiedliche Basis-Regeln feuern, lohnt sich eine Normalisierung:

  1. Erstelle eine eigene Rule, die alle relevanten Auth-Fail-Events zuverlässig matcht (z. B. per <if_group> / <if_sid> / <match> je nach Quelle) und einheitlich taggt.
  2. Korrigiere/normalisiere Felder über Decoder/Integration (z. B. sicherstellen, dass dstuser überall befüllt ist).
  3. Korrelieren dann ausschließlich auf diese Rule-ID via <if_matched_sid>.

Das reduziert Pflegeaufwand erheblich.

Workaround 3: Feldvalidierung vor Korrelation erzwingen

Wenn du same_field nutzt, stelle sicher, dass das Feld für alle korrelierten Events existiert. Eine robuste Technik ist eine zusätzliche Bedingung:

<rule id="555105" level="10" frequency="4" timeframe="120">
  <if_matched_sid>BASE_RULE_IDs</if_matched_sid>
  <field name="dstuser">\S+</field>
  <description>Viele fehlgeschlagene Authentifizierungen für denselben Benutzer</description>
  <same_field>dstuser</same_field>
</rule>

Damit korrelierst du nicht auf Events, in denen der User leer/uneinheitlich ist.


Lessons Learned / Best Practices

  • Korrelation auf Gruppenbasis ist in der aktuellen wazuh-analysisd-Engine nicht in allen Fällen zuverlässig; für produktive Korrelationen ist <if_matched_sid> die sicherere Wahl.
  • Feldnamen zuerst verifizieren: Nutze wazuh-logtest bzw. den tatsächlichen Rule-Context, nicht nur den Alert-JSON-Output. Entscheidend ist, ob srcip, dstuser, user im Decoder-Kontext existieren.
  • Baue Korrelationen gegen eine normalisierte Basisregel, wenn du mehrere Systeme/Quellen zusammenführen willst.
  • Setze Schwellenwerte (frequency/timeframe) so, dass sie echte Angriffe abbilden und nicht durch reguläre Fehlbedienung feuern.
  • Plane mittelfristig damit, dass die Engine-Weiterentwicklung (Wazuh Engine als Nachfolger von analysisd) diese Klasse von Einschränkungen deutlich entschärfen soll; bis dahin sind explizite Rule-ID-Korrelationen das stabilste Muster.

Referenzen (Engine-/Korrelationseinschränkungen):

  • GitHub Issue: 27488
  • GitHub Issue: 3291
  • GitHub Issue: 29585

Fazit

Wenn „Many failed authentication“-Korrelationen in Wazuh nicht triggern, obwohl Basis-Alerts vorhanden sind, liegt die Ursache häufig nicht in frequency/timeframe, sondern in der Art, wie wazuh-analysisd Korrelationen über Gruppen behandelt. Der praxistaugliche Workaround ist, konsequent auf <if_matched_sid> zu wechseln und auf die Rule-IDs der Basis-Detections zu korrelieren – ergänzt um saubere Feldnormalisierung (z. B. dstuser) und Feldvalidierung. Damit bekommst du robuste Brute-Force-/Password-Spraying-Detections über heterogene Systeme hinweg.


Mehr zu Wazuh …
https://wazuh.com/?utm_source=ambassadors&utm_medium=referral&utm_campaign=ambassadors+program

Mehr zum Wazuh Ambassador Program …
https://wazuh.com/ambassadors-program/?utm_source=ambassadors&utm_medium=referral&utm_campaign=ambassadors+program

https://wazuh.slack.com/archives/C0A933R8E/p1768557314871879