Skip to content

systemd-Journal-Forensik: Dateien, Seqnum-Lücken, .journal~

Forensik von systemd-Journal-Dateien ohne journalctl: Dateiformat, vertrauenswürdige Felder, Kompression, Seqnum-Lücken, .journal~-Dateien und Zeitfelder.

Veröffentlicht am 5 Min. Lesezeit

Kurzfassung. Eine Journal-Datei ist eine binäre, nur anhängend beschriebene Datenbank mit der Signatur LPKSHHRH. Jeder Eintrag trägt eine Sequenznummer, die journald über alle Dateien eines Systems hinweg hochzählt; ein fehlender Nummernbereich bleibt daher sichtbar, selbst wenn die Einträge verschwunden sind. Lesen Sie die Dateien selbst, nicht nur einen journalctl-Export: In Dateiköpfen, Sequenznummern, .journal~-Dateien und Benutzer-Journalen zeigen sich Löschungen.

Wo die Dateien liegen

PfadInhalt
/var/log/journal/<machine-id>/system.journalAktives System-Journal (persistente Speicherung)
/var/log/journal/<machine-id>/system@<id>-<seqnum>-<time>.journalArchivierte (rotierte) System-Journale
/var/log/journal/<machine-id>/user-<UID>.journalBenutzer-Journal für reguläre UIDs (Standard SplitMode=uid)
*.journal~Dateien, die nach einem unsauberen journald-Stopp oder erkannter Beschädigung umbenannt wurden
/run/log/journal/<machine-id>/Flüchtiges Journal, beim Neustart verloren

<machine-id> entspricht /etc/machine-id. Ob überhaupt etwas nach /var geschrieben wird, hängt von Storage= in journald.conf ab: Lesen Sie die Konfiguration im Abbild, statt es vorauszusetzen.

Warum sich der Aufwand mit dem Journal lohnt

Das systemd-Journal speichert jeden Eintrag als FIELD=value-Paare. Die Felder, die mit einem Unterstrich beginnen, fügt journald aus der Sicht des Kernels auf den Absender hinzu, daher kann der sendende Prozess sie nicht fälschen:

  • _PID, _UID, _GID, _COMM, _EXE, _CMDLINE: wer die Meldung tatsächlich gesendet hat.
  • _SYSTEMD_UNIT, _SYSTEMD_USER_UNIT: von welchem Dienst oder Benutzerdienst sie stammt.
  • _BOOT_ID: aus welchem Systemstart; Neustarts werden zu expliziten Grenzen.
  • _AUDIT_SESSION, _AUDIT_LOGINUID: Audit-Sitzung und Login-UID, die den Eintrag mit ses und auid von auditd verknüpfen.
  • _TRANSPORT: syslog, journal, stdout, kernel, audit, driver.

MESSAGE, SYSLOG_IDENTIFIER und PRIORITY liefert der Client. Jeder kann mit logger sshd: Accepted password ... schreiben; die Unterstrich-Felder zeigen dann, dass die Meldung von logger unter der UID dieses Benutzers kam.

Das Dateiformat im Überblick

  • Header. Signatur LPKSHHRH, kompatible und inkompatible Feature-Flags, state (offline, online, archived), machine_id, seqnum_id, head_entry_seqnum und tail_entry_seqnum, erster und letzter Realtime-Zeitstempel, Boot-ID des letzten Eintrags sowie Objektzähler. Aktuelles systemd schreibt einen 272-Byte-Header; Leser müssen die in der Datei gespeicherte Header-Größe verwenden, da ältere Dateien kürzere Header haben.
  • Objekte. Nach dem Header folgen typisierte Objekte: DATA (eine field=value-Nutzlast, per Hash dedupliziert), FIELD, ENTRY (die Liste der DATA-Objekte, aus denen ein Eintrag besteht, mit seqnum, Realtime, Monotonic und Boot-ID), ENTRY_ARRAY, Hashtabellen und, mit Forward Secure Sealing, TAG.
  • Kompression. Große DATA-Nutzlasten können mit XZ, LZ4 oder ZSTD komprimiert sein, gekennzeichnet pro Objekt. Die LZ4-Variante speichert die unkomprimierte Größe als 64-Bit-Little-Endian-Ganzzahl vor dem LZ4-Block.
  • Compact-Modus. Seit systemd 252 können Dateien das kompakte Layout mit 32-Bit- statt 64-Bit-Offsets verwenden. Ältere journalctl-Versionen verweigern solche Dateien, ein Grund mehr, einen aktuellen Leser zu verwenden.

Ein Parser, der die Objekte direkt durchläuft, kann Einträge lesen, die journalctl überspringen würde, zum Beispiel aus einer Datei, deren Header nach einem Absturz nicht aktualisiert wurde.

Sequenznummern: der eingebaute Manipulationsnachweis

Jeder Eintrag hat eine seqnum. journald erhöht einen Zähler pro System (identifiziert durch seqnum_id) und verwendet ihn für alle Dateien, die es schreibt, System- wie Benutzerdateien. Sortieren Sie jeden Eintrag aus jeder Datei innerhalb einer seqnum_id nach seqnum, sollten die Nummern lückenlos sein. Eine Lücke in der Journal-seqnum bedeutet, dass Einträge, die einmal existierten, in Ihren Dateien fehlen.

Zuerst die harmlosen Ursachen:

  • Benutzer-Journale nicht gesichert. Einträge, die in user-1001.journal geschrieben wurden, verbrauchen ebenfalls Nummern. Wurde nur system.journal gesichert, erscheint jeder Benutzereintrag als Lücke.
  • Per Vacuum entfernte Archive. journalctl --vacuum-* und größenbasierte Rotation löschen alte archivierte Dateien; die Lücke liegt dann am Anfang des aufbewahrten Bereichs.
  • Rotationsgrenzen zwischen einem Archiv, das Sie haben, und einem, das Ihnen fehlt.

Interessant ist eine Lücke innerhalb des Vorfallszeitraums, in einem Satz, in dem alle Benutzer-Journale vorhanden sind, oder eine Lücke, die zu einem von Hand gelöschten Benutzer-Journal passt. Im Beispiel des Tools entfernt der Angreifer user-1001.journal: Das System-Journal behält seine Nummern, aber die Einträge, die in die Benutzerdatei gingen, fehlen, und die darin enthaltenen Zeilen des User-Managers überleben nur in /var/log/syslog (rsyslog hat sie ebenfalls empfangen).

Unsaubere und geöffnete Dateien

  • Eine .journal~-Datei wurde umbenannt, weil journald sie nicht sauber geschlossen oder als beschädigt erkannt hat. Sie enthält oft die letzten Minuten vor einem Absturz oder harten Ausschalten. Parsen Sie sie; das Ende kann teilweise geschrieben sein.
  • Ein Header-state online in einer gesicherten Datei bedeutet, dass journald sie geöffnet hatte. Das ist normal für ein von einem laufenden Host kopiertes system.journal, aber die letzten Einträge können unvollständig sein.
  • Eine Datei, deren Header mehr Einträge angibt, als sich lesen lassen, oder deren Ende beschädigt ist, gehört als Hinweis in den Bericht, nicht in den Papierkorb.

Zeitfelder

FeldBedeutung
__REALTIME_TIMESTAMPZeitpunkt, zu dem journald den Eintrag empfangen hat, Mikrosekunden seit der Epoche (UTC)
_SOURCE_REALTIME_TIMESTAMPFrüheste vertrauenswürdige Zeit der Meldung an ihrer Quelle, wenn sie vom Empfang abweicht; journalctl zeigt diese an, sofern vorhanden
__MONOTONIC_TIMESTAMPMikrosekunden seit dem Systemstart, aussagekräftig zusammen mit _BOOT_ID

Realtime-Werte folgen der Systemuhr. Eine zurückgestellte Uhr zeigt sich daran, dass Realtime sinkt, während seqnum steigt; innerhalb eines Systemstarts gibt die Monotonic-Uhr die wahre Reihenfolge an.

Exporte lesen

Sind keine Rohdateien verfügbar, ist journalctl -o export pro Eintrag verlustfrei (Binärwerte sind längenpräfixiert), und -o json ist bequem. Beide verlieren die Dateiköpfe und sind auf das beschränkt, was journalctl lesen konnte. Siehe den Leitfaden zur Sicherung.

journalctl -D /mnt/evidence/var/log/journal --header       # Dateibereiche, Status, seqnum-IDs
journalctl -D /mnt/evidence/var/log/journal --list-boots --utc
journalctl -D /mnt/evidence/var/log/journal -o export > journal.export

Im Browser

Linux Log Parser parst reguläre und kompakte Journal-Dateien, mit XZ/LZ4/ZSTD komprimierte Felder, .journal~-Dateien und Exporte, meldet Sequenznummernlücken (und warnt, wenn kein Benutzer-Journal geladen wurde), kennzeichnet unsaubere und geöffnete Dateien und markiert auth.log-Zeilen, die auch im Journal stehen. Auth-Zeilen, die im Journal stehen, aber in auth.log fehlen, werden als eigener Befund gemeldet; mehr dazu im Leitfaden zur Manipulationserkennung.

Der Spickzettel zum systemd-Journal auf linuxforensics.app listet die Aufbewahrungseinstellungen und journalctl-Befehle im Detail auf.

Verwandte Artikel

So belegen Sie Bearbeitung oder Löschung von Linux-Logs: seqnum-Lücken, in auth.log fehlende Zeilen, geleerte wtmp-Einträge, gestoppte Daemons, Löschbefehle.
Was Sie für eine Linux-Log-Ermittlung sichern und wie: ein tar-Befehl am laufenden Host, journalctl-Export, ausearch, UAC, Velociraptor oder ein Abbild.
Eine Linux-Log-Ermittlung auf einem synthetischen Jump-Host: SSH-Passwortraten, Login von neuer IP, sudo -i, Persistenz über cron und systemd, Log-Manipulation.