Costantino wollte SFTP-Transfers auf einem Linux-Server sauber in Wazuh auswerten.
Die Logs sehen in etwa so aus:
Mon Nov 17 15:00:02 2025 0 host.example.local 123456 /sftp/user/project/Out/FileFeedback/file_skipped.json b _ i r project_user sftp 0 * c
Mit anderen Worten:
- Datum/Zeit
- Session-ID
- Host
- Bytes
- kompletter Dateipfad
- Transfer-Flags (
b _ i r) - User, Service, Response Code, Remote Host, Status (
c)
Costantino wollte daraus u. a. folgende Felder holen:
session_idsrc_ip/ Hostbytesdirectoryfilenameextensiontransfer_type,direction,modeuser,service,response_code,remote_host,completion_status
Sein erster Ansatz: sehr komplexe Regexe mit Timestamp am Anfang und jede Menge Child-Decoder. Ergebnis:
Logs wurden nicht oder nur teilweise korrekt decodiert.
Schauen wir uns an, warum – und wie Bony John das in eine klare, stabile Lösung verwandelt hat.
1. Stolperfalle: Der Timestamp ist in Wazuh bereits „weg“
Costantinos ursprünglicher Decoder versuchte, die komplette Zeile inklusive Datum zu matchen, z. B.:
<prematch type="pcre2">
^\w{3}\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4}\s+\d+\s+\S+\s+\d+\s+
</prematch>
und ähnliche Muster im <regex>.
Aber:
In Wazuh wird ein Teil des Logs – insbesondere der Timestamp – schon in Phase 1 (pre-decoding) verarbeitet und in das Feld timestamp ausgelagert.
Phase-1-Ausgabe sah etwa so aus:
**Phase 1: Completed pre-decoding.
full event: 'Mon Nov 17 15:00:03 2025 0 host.example.local 123 /sftp/user/project/Out/FileFeedback/file.csv b _ i r project_user sftp 0 * c'
timestamp: 'Mon Nov 17 15:00:03 2025'
Das heißt:
- Dein Decoder muss nicht mehr am absoluten Anfang der Rohzeile ansetzen.
- Er sollte den Teil matchen, der nach der Wazuh-Vorverarbeitung übrig bleibt – also ab
0 host.example.local 123 /sftp/....
Wer trotzdem versucht, „ab Anfang der Zeile“ mit Datum zu matchen, schießt sich damit selbst ins Knie – die Regex trifft dann nicht oder nur noch teilweise.
2. Der entscheidende Schritt: Ein einziger, sauberer Decoder statt Decoder-Kaskade
Ursprünglich hatte Costantino viele Child-Decoder:
- einer für Session-ID & IP
- einer für Bytes
- einer für Directory & Filename
- einer für Flags
- einer für User / Service / Status
Bony hat das deutlich vereinfacht und gezeigt:
Wenn das Logformat stabil ist, ist ein einziger Decoder mit einem gut strukturierten Regex die deutlich bessere Lösung.
2.1 Erste funktionierende Fassung
Die erste Version von Bony:
<decoder name="sftp-decoder">
<prematch>^\d*\s*\S*\s*\d*\s*\S*</prematch>
<regex>^(\d*)\s*(\S*)\s*(\d*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)</regex>
<order>session_id,src_ip,bytes,file,transfer_type,connection_type,direction,mode,user,service,response_code,remote_host,completion_status</order>
</decoder>
Damit wurde bereits:
- Session-ID
- Host (
src_ip) - Bytes
- gesamte
file-Spalte - sowie Flags, User, Service, Code, Remote Host, Status
korrekt extrahiert.
Wichtiger Lerneffekt:
- Die
<prematch>ist bewusst generisch gehalten und fängt erst hinter dem vom Predecoder verwendeten Teil an. - Die Regex matcht nicht den Timestamp, sondern den eigentlichen Payload-Anteil.
3. Zusatzanforderung: Directory, Dateiname, Extension getrennt aus dem Pfad ziehen
Später wollte Costantino mehr:
directory(Pfad ohne Datei)filename(ohne Endung)extension(z. B.csv,json)
Erst versuchte er, das über child decoders nachzuziehen:
<decoder name="sftp-decoder-son">
<parent>sftp-decoder</parent>
<regex type="pcre2">\/[^\/]+\/[^\/]+\/([^\/]+)\/</regex>
<order>third_directory</order>
</decoder>
...
und ähnliche Muster für filename und file_extension.
Das Problem dabei:
- Child-Decoder sind eher dafür gedacht, zusätzliche Felder aus dem vollen Log oder aus Teilsegmenten herauszulösen.
- In diesem Fall ist es aber viel sauberer, den Pfad direkt im Hauptdecoder fein aufzuteilen.
Bony hat das dann genau so umgesetzt.
3.1 Endgültige, aufgesplittete Version
<decoder name="sftp-decoder">
<prematch>^\d*\s*\S*\s*\d*\s*\S*</prematch>
<regex>
^(\d*)\s*(\S*)\s*(\d*)\s*(\S*)/(\w*).(\w*)\s*
(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)
</regex>
<order>
session_id,
src_ip,
bytes,
directory,
filename,
extension,
transfer_type,
connection_type,
direction,
mode,
user,
service,
response_code,
remote_host,
completion_status
</order>
</decoder>
Was hier passiert:
(\d*)→session_id(\S*)→src_ip(host.example.local)(\d*)→bytes(\S*)/(\w*).(\w*)→directory=/sftp/user/project/Out/FileFeedbackfilename=file_skippedextension=json
- der Rest jeweils ein Feld (
b,_,i,r,project_user,sftp,0,*,c)
Damit stehen alle wichtigen Informationen sofort als Felder zur Verfügung – ganz ohne zusätzliche Child-Decoder.
4. Warum ein einzelner Decoder hier die beste Lösung ist
Vorteile:
- Weniger Komplexität: Ein einziges Regex-Muster pro Logtyp
- Besser wartbar: Änderungen an Logformat = eine Stelle anpassen
- Schneller: Wazuh muss nicht mehrere Decoder-Kaskaden testen
- Robuster: Weniger Risiko, dass ein Child-Decoder unerwartet nicht matched
Wann sich Child-Decoder lohnen:
- Wenn du wirklich verschiedene Formate hast, die sich nur teilweise überschneiden
- Wenn du aufeinander aufbauende Analyse-Stufen brauchst (erst basic, dann optional weitere Details)
In diesem Fall war das Format aber so konsistent, dass die „all-in-one“-Variante klar im Vorteil ist.
5. Praxistipps für eigene SFTP-/Custom-Logs in Wazuh
1. Phase 1 checken
Immer zuerst mit wazuh-logtest prüfen, was Phase 1 macht:
- Wie sieht
full event:aus? - Was wurde bereits in
timestamp,hostname, etc. ausgelagert?
2. Regex NIE stumpf am Anfang der Originalzeile ausrichten
Wenn Wazuh Vorverarbeitung macht, solltest du nicht mit ^Mon Nov ... matchen, sondern mit dem Teil nach dem Timestamp.
3. Möglichst viel in einem Decoder erledigen
Wenn der Logaufbau stabil ist, lieber einen etwas längeren, aber klaren Regex statt 10 Einzelschnipsel verwenden.
4. Wazuh-Doku für Regex & Decoder nutzen
Wazuh hat eigene Beispiele & Besonderheiten:
RegEx-Doku & Decodersyntax sind Gold wert – „irgendwelche“ generischen Regex-Tipps (oder AI-Ausgaben 😉) sind oft knapp daneben.
6. Fazit
Costantinos SFTP-Decoder-Problem zeigt:
- Man kann sich mit zu komplexen, timestamp-basierten Regexes schnell selbst blockieren.
- Wazuhs Predecoder nimmt dir den ersten Teil (Timestamp) bereits ab – du musst ihn nicht nochmal matchen.
- Mit einem einzigen, gut geschriebenen Decoder lassen sich SFTP-Logs sehr sauber in Felder wie
directory,filename,extension,user,direction,statusetc. zerlegen.
Damit wird aus einem „unförmigen“ Log:
Mon Nov 17 15:00:02 2025 0 host.example.local 123456 /sftp/user/project/Out/FileFeedback/file_skipped.json b _ i r project_user sftp 0 * c
ein strukturierter Datensatz – ideal für Dashboards, Alerts und Reports.
https://wazuh.slack.com/archives/C0A933R8E/p1763461364773649