Wazuh HTTP-Decoding erweitern: Warum dein Custom-Decoder nicht greift – und wie du Referer/User-Agent sauber extrahierst


Einleitung

HTTP-Access-Logs sind in Wazuh häufig der Einstiegspunkt für Web- und AppSec-Korrelationen: Brute-Force, Scans, verdächtige User-Agents, Exploit-Versuche und Policy-Verstöße werden oft erst durch sauber extrahierte Felder wirklich auswertbar. In der Praxis scheitert die Erweiterung bestehender Decoder jedoch oft an einem Detail: der Decoder-Pipeline und der Art, wie Wazuh Parent-, Child- und „Sibling“-Decoder auswertet. Dieser Beitrag zeigt, warum ein scheinbar korrekt definierter Custom-Decoder nicht greift – und welche belastbaren Wege es gibt, Referer und User-Agent zuverlässig zu extrahieren.


Ausgangslage / Problemstellung

Eine Umgebung verarbeitet klassische Nginx/Apache-Access-Logs im Combined-Format, z. B.:

192.168.17.250 - - [13/Jan/2026:19:23:45 +0000] "POST /zabbix.php?action=notifications.get&output=ajax HTTP/1.1" 200 565 "https://zabbix.example.com/..." "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0"

Ziel: Referer und User-Agent als zusätzliche Felder extrahieren, wenn die Standard-Regel/Decoder-Familie für web-accesslog bereits matched.

Ein Custom-Decoder wird unter local_decoders.xml angelegt, mit Parent web-accesslog. In wazuh-logtest erscheint jedoch kein neues Feld – als ob der Decoder nie geladen oder nie ausgewertet würde.


Technische Analyse

1) Decoder-Auswertung: „Ein Treffer – und Schluss“

Wazuh-Decoder sind hier nicht wie viele Parser-Pipelines („mehrere Decoder matchen nacheinander“) aufgebaut. Bei Web-Access-Logs passiert typischerweise Folgendes:

  • Ein Parent-Decoder (web-accesslog) führt zum passenden Decoder-„Stamm“.
  • Danach greifen Child-Decoder, um Varianten abzudecken (z. B. IP-basiert, Hostname-basiert, spezielle Formate).
  • Sobald ein passender Child-Decoder gefunden wurde (z. B. web-accesslog-ip), wird nicht automatisch noch ein weiterer „Sibling“ ausgewertet.

Das erklärt den Effekt: Wenn dein Log bereits von einem bestehenden Child-Decoder wie web-accesslog-ip erkannt wird, kommt dein zusätzlicher Decoder nicht mehr zum Zug.

2) „Sibling“-Decoder sind keine name-freien Geschwister, sondern dieselbe Decoder-Identität

In Wazuh ist „Sibling“ nicht „anderer Decoder mit anderem Namen“, sondern eine Gruppe mehrerer Decoder-Definitionen, die denselben name tragen. Diese Technik wird genutzt, um mehrere Regex-Varianten unter einem Decoder-Namen bereitzustellen (optional vorhandene Felder, leicht wechselnde Feldreihenfolgen etc.).

Ein Decoder mit neuem Namen wie web-accesslog-referer ist damit kein Sibling, sondern ein normaler zusätzlicher Child-Decoder – und wird in der Praxis nur dann geprüft, wenn vorherige Child-Decoder nicht matchen.

3) Datei-Reihenfolge und „Decoder Family“-Grenzen

Decoder werden zudem in Dateireihenfolge geladen und ausgewertet. Das erlaubt einen pragmatischen Workaround: Eine frühere Datei (alphabetisch kleiner) kann einen passenderen Decoder bereitstellen, bevor der Standard-Decoder greift.


Lösung / Best Practices

Es gibt drei robuste Strategien – je nach gewünschtem Eingriffstiefen- und Wartungsniveau.

Option A: Custom-Decoder vor Standard-Decodern auswerten (empfohlen, minimal-invasiv)

Lege eine zusätzliche Decoder-Datei an, die vor der Standard-Datei geladen wird, z. B.:

/var/ossec/etc/decoders/0374-web-accesslog-ext-decoders.xml

Dort definierst du einen präzisen Decoder, der Combined-Logs (inkl. Referer/User-Agent) sauber matcht und Felder extrahiert. Wichtig: Möglichst spezifische prematch/Regex, damit du keine anderen Weblog-Varianten störst.

Beispiel (an Combined-Format orientiert, mit klarer Feldextraktion):

<decoder name="web-accesslog-ip-referer">
  <type>web-log</type>
  <prematch>^\S+ \S+ \S+ \[.*\] "\w+ \S+ HTTP\/[0-9.]+" \d+ \d+ ".*" ".*"</prematch>
  <regex>^(\S+) \S+ \S+ \[.*\] "(\w+) (\S+) HTTP\/[0-9.]+" (\d+) \d+ "(.*)" "(.*)"$</regex>
  <order>srcip, protocol, url, id, referer, user-agent</order>
</decoder>

Zusatz: Stelle sicher, dass Datei und Inhalt dem Wazuh-User gehören:

  • Owner/Group: wazuh:wazuh

Anschließend:

  • systemctl restart wazuh-manager
  • Test mit /var/ossec/bin/wazuh-logtest

Vorteil: Du musst Standard-Dateien nicht anfassen und gewinnst trotzdem Kontrolle über die Decoder-Priorität.

Option B: Standard-Decoder ersetzen (sauber, aber wartungsintensiver)

Wenn du den Standard-Decoder wirklich umbauen willst (z. B. mehrere Varianten als Siblings innerhalb derselben Decoder-Familie), dann ist der belastbare Weg:

  1. Standard-Datei aus dem Ruleset nach etc/decoders kopieren (niemals im Ruleset selbst ändern).
  2. Original-Datei per <decoder_exclude> in ossec.conf deaktivieren.
  3. In der kopierten Datei gezielt Decoder erweitern oder Sibling-Varianten hinzufügen.

Beispiel-Konzept:

  • Datei kopieren nach: /var/ossec/etc/decoders/web-accesslog_decoders.xml
  • In /var/ossec/etc/ossec.conf:
<ruleset>
  <decoder_dir>etc/decoders</decoder_dir>
  <rule_dir>etc/rules</rule_dir>
  <decoder_exclude>0375-web-accesslog_decoders.xml</decoder_exclude>
</ruleset>

Dann in der kopierten Decoder-Datei eine zusätzliche Regex-Variante als Sibling unter gleichem Namen platzieren (gleicher name, mehrere Definitionen).

Vorteil: Vollständige Kontrolle.
Nachteil: Du trägst die Decoder-Familie selbst und musst bei Upgrades vergleichen/mergen.

Option C: out_format nutzen und komplett eigene Decoder-Familie aufbauen

Das ist sinnvoll, wenn du die Standard-web-accesslog-Kette bewusst umgehen willst. Per out_format kannst du das Log „präfixen“ oder normalisieren, sodass es nicht mehr auf die Standard-Decoder passt. Dann baust du eine eigene Decoder-Pipeline (und ggf. eigene Regeln). Das ist die flexibelste, aber auch aufwendigste Variante.


Lessons Learned / Best Practices

  • Decoder-Matching ist nicht additiv: Ein früher Treffer verhindert spätere Decoder-Auswertung.
  • „Sibling“ bedeutet: gleicher Decoder-Name, nicht „gleicher Parent“ oder „gleiches Thema“.
  • Nutze Dateipriorität (alphabetische Reihenfolge), um Custom-Decoding „vor“ Standard-Decoding zu erzwingen.
  • Verwende prematch so spezifisch wie möglich, um Seiteneffekte zu vermeiden.
  • Änderungen an Decodern sind immer ein Manager-Thema (nicht Agent): Decoder laufen serverseitig, daher wazuh-manager neu starten.
  • Teste Änderungen konsequent mit wazuh-logtest und halte ein Set repräsentativer Beispielzeilen vor (mit/ohne Referer, mit/ohne UA).

Fazit

Wenn dein Custom-Decoder bei web-accesslog nicht greift, liegt das in der Regel nicht an „nicht geladen“, sondern an der Decoder-Logik: Ein vorhandener Child-Decoder matcht bereits, und Wazuh wertet keine weiteren Decoder in dieser Kette aus. Der pragmatischste Weg ist, einen spezifischen Custom-Decoder in einer früher geladenen Datei zu platzieren. Alternativ kannst du die Standard-Decoder-Familie kontrolliert ersetzen oder komplett eigene Decoder mit out_format aufbauen. So bekommst du Referer und User-Agent zuverlässig in die Eventfelder – und damit die Grundlage für aussagekräftigere Detection- und Hunting-Workflows.


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/p1768485481774999