Zum Inhalt springen

AusführungBenutzeraktivitätAnti-Forensik

Shell-History: Befehlslogs von bash, zsh und fish

Befehlshistorie pro Benutzer von bash, zsh und fish unter Linux: was jede Datei erfasst, wann es Zeitstempel gibt und wie sich Umgehung zeigt.

Speicherort
~/.bash_history
Belegt
Welche Befehle in einer interaktiven Shell unter einem bestimmten Konto eingegeben wurden, manchmal auch wann
Zeitstempel
Sekunden seit der Unix-Epoche (UTC), sofern erfasst: bash nur mit HISTTIMEFORMAT, zsh mit EXTENDED_HISTORY, fish immer
Zugriff
Jeder Benutzer für die eigenen Dateien; root für die Home-Verzeichnisse anderer Benutzer
Aufbewahrung
Bis zur Kürzung durch HISTFILESIZE/SAVEHIST oder bis zur Löschung
Sicherung
UAC, Velociraptor, cp -a, tar

Was es ist

Interaktive Shells führen eine Liste der Befehlszeilen, die ein Benutzer eingegeben hat, und speichern sie in einer Datei pro Benutzer, damit sie in späteren Sitzungen wieder abrufbar sind. Bash, zsh und fish verwenden jeweils eine eigene Datei und ein eigenes Format. Die Dateien sind Klartext (fish nutzt ein YAML-ähnliches Layout), gehören dem Benutzer, sind für ihn beschreibbar und werden nach dem eigenen Rhythmus der Shell geschrieben, nicht bei jedem ausgeführten Befehl.

Für Ermittler ist das nach einer Kontoübernahme oft der direkteste Nachweis von Hands-on-Keyboard-Aktivität. Zugleich ist sie konstruktionsbedingt unvollständig: Was die Shell nie auf die Platte geschrieben hat oder was der Benutzer unterdrückt hat, fehlt schlicht.

Wo es liegt

ShellStandarddateiHinweise
bash~/.bash_historyPfad über HISTFILE festgelegt; ist HISTFILE nicht gesetzt oder leer, wird beim Beenden nichts gespeichert
bash (root)/root/.bash_historyHier landen Befehle nach sudo -i, sudo su - oder su -
zshKein Standard; üblich sind ~/.zsh_history oder ~/.zhistoryWird nur geschrieben, wenn HISTFILE in einer Startdatei gesetzt ist (und SAVEHIST ungleich null)
fish~/.local/share/fish/fish_history$XDG_DATA_HOME/fish/, falls gesetzt; die Variable fish_history ändert den Sitzungsnamen in <name>_history, ein leerer Wert deaktiviert das Speichern

Die Pfade sind unter Debian/Ubuntu, RHEL/Fedora und anderen Familien gleich. Unterschiedlich ist die Standardkonfiguration: So setzt die Skeleton-~/.bashrc von Debian und Ubuntu HISTCONTROL=ignoreboth und aktiviert histappend, während andere Distributionen History-Variablen in /etc/profile, /etc/bashrc oder /etc/profile.d/ setzen können. Lesen Sie immer die tatsächlichen Startdateien im Image (siehe Shell-Startdateien), bevor Sie Lücken interpretieren.

Auch Dienstkonten haben Home-Verzeichnisse: Prüfen Sie /var/www, /var/lib/postgresql, /var/lib/mysql, /opt/* und jedes in /etc/passwd eingetragene Home-Verzeichnis auf verstreute History-Dateien.

Was es belegt

  • Welche Befehlszeilen in einer interaktiven Shell unter diesem Konto eingegeben wurden.
  • Die relative Reihenfolge, in der eine Shell diese Zeilen geschrieben hat (bei parallelen Sitzungen nicht immer die Ausführungsreihenfolge).
  • Mit Zeitstempeln: wann jeder Befehl eingegeben wurde (bei zsh und fish lässt sich außerdem die Sitzungsaktivität eingrenzen).
  • Verwendete Werkzeuge, Dateipfade, entfernte Hosts, URLs und Zugangsdaten in Argumenten, die auf weitere Artefakte verweisen.

Es belegt nicht:

  • Wer an der Tastatur saß (geteilte, gestohlene oder per su gewechselte Konten brechen die Zuordnung).
  • Dass ein Befehl erfolgreich war oder überhaupt lief, wenn er eingetippt und dann abgebrochen wurde.
  • Irgendetwas über nicht interaktive Ausführung: Skripte, Cronjobs, Webshells und ssh host 'cmd' hinterlassen hier normalerweise nichts.

Wichtige Felder

FormatAufbau des EintragsBedeutung
bash, einfachcommand lineEin Eintrag pro Zeile, ohne Zeit
bash, HISTTIMEFORMAT gesetzt#1727512345, dann command lineKommentarzeichen plus Ziffern markiert einen Zeitstempel für den folgenden Eintrag; grenzt zugleich mehrzeilige Einträge ab
zsh EXTENDED_HISTORY: <start>:<elapsed>;commandStartzeit in Sekunden seit der Epoche, verstrichene Sekunden, dann der Befehl
fish- cmd: ... / when: ... / optionale Liste paths:when sind Sekunden seit der Epoche; paths listet Argumente, die fish als existierende Pfade erkannt hat

Zsh speichert Nicht-ASCII-Bytes in einer "metafied" Kodierung, daher wirken manche Zeichen in einem einfachen Textbetrachter beschädigt.

Zeitstempel

Alle drei Formate verwenden Sekunden seit der Unix-Epoche, also UTC. Die mtime der Datei zeigt, wann eine Shell sie zuletzt geschrieben hat, die ctime die letzte Metadatenänderung (nützlich, wenn eine Datei gekürzt oder durch einen Symlink ersetzt wurde).

# bash with HISTTIMEFORMAT
grep -E '^#[0-9]{9,}$' .bash_history | head -1 | cut -c2- | xargs -I{} date -u -d @{}   # first timed entry
# zsh extended history
awk -F'[:;]' '/^: [0-9]+:/ {cmd="date -u -d @" ($2+0) " +%FT%TZ"; cmd | getline t; close(cmd); print t, $0}' .zsh_history

Aufbewahrung

  • bash: Beim Beenden werden die letzten HISTSIZE Einträge (Standard 500) angehängt (mit histappend) oder überschreiben die Datei, die anschließend auf HISTFILESIZE Zeilen gekürzt wird. Alte Aktivität fällt oben heraus.
  • zsh: SAVEHIST begrenzt die Datei. APPEND_HISTORY ist standardmäßig aktiv; INC_APPEND_HISTORY oder SHARE_HISTORY schreiben jeden Befehl sofort statt erst beim Beenden.
  • fish: history clear und history delete entfernen Einträge; history merge übernimmt Änderungen anderer Sitzungen.
  • Sitzungen, die mit SIGKILL beendet wurden, abgestürzt sind oder zum Zeitpunkt der Sicherung noch offen waren, erreichen die Platte womöglich nie. Eine laufende bash hält sie im Prozessspeicher.

Sicherung

Sichern Sie aus jedem Home-Verzeichnis und aus /root, einschließlich rotierter oder gesicherter Kopien (.bash_history~, .bash_history.*). Die UAC-Artefakte files/shell/* (Teil von ir_triage) decken bash, zsh, fish und weitere Shells ab und werten zudem Startdateien aus, um ein vom Standard abweichendes HISTFILE zu finden.

sudo ./uac -p ir_triage /mnt/usb/case42
# dead box, read-only mount
find /mnt/evidence/root /mnt/evidence/home -maxdepth 4 \
  \( -name '.*history*' -o -path '*/fish/fish_history' \) -print0 | tar --null -T - -czf history.tgz

In Velociraptor durchsucht Linux.Sys.BashHistory /{root,home/*}/.*_history auf allen Endpunkten. Wenn die verdächtige Shell noch laufen könnte, sichern Sie zuerst den Arbeitsspeicher (LiME oder AVML): Die Sicherung der Platte schreibt die History aus dem Speicher nicht weg, eine spätere Abmeldung überschreibt aber, was Sie gesehen haben.

Auswertung

  • Plaso: Die Text-Plugins bash_history und zsh_extended_history sind im Preset linux enthalten (Auswahl als text/bash_history und text/zsh_extended_history); fish_history ist ein separater Parser. Nur Einträge mit Zeitstempel werden zu Ereignissen.
  • Volatility 3: linux.bash.Bash stellt die History aus laufenden bash-Prozessen wieder her, einschließlich Befehlen, die nie auf die Platte geschrieben wurden.
  • Für die Durchsicht genügen einfache Werkzeuge:
log2timeline.py --parsers 'text/bash_history,text/zsh_extended_history,fish_history' \
  --storage-file hist.plaso /mnt/evidence/home
psort.py -o dynamic --output_time_zone UTC -w hist.csv hist.plaso

Tipps für die Untersuchung

  • Werten Sie eine leere, null Byte große oder per Symlink auf /dev/null zeigende History bei einem aktiven Konto als Spur. Prüfen Sie die ctime des Symlinks und vergleichen Sie die mtime der Datei mit Anmeldungen in wtmp und lastlog.
  • Suchen Sie in den Historien anderer Shells, in Startdateien und im Arbeitsspeicher nach Umgehungsbefehlen: unset HISTFILE, HISTFILE=/dev/null, HISTSIZE=0, set +o history, history -c, history -d, kill -9 $$.
  • Fehlende Befehle, die mit führendem Leerzeichen eingegeben wurden, sind zu erwarten, wenn ignorespace/ignoreboth oder bei zsh HIST_IGNORE_SPACE gesetzt ist, und fish verwirft solche Zeilen grundsätzlich. Das ist eine Voreinstellung, kein Beweis für Absicht.
  • Sind in einer bash-Datei nur teilweise #epoch-Zeilen vorhanden, war HISTTIMEFORMAT irgendwann aktiviert; Zeilen ohne Zeit sind älter oder stammen aus einer Shell ohne diese Einstellung.
  • Machen Sie nach sudo -i oder su - in /root/.bash_history weiter und korrelieren Sie mit den sudo-Logs.
  • Wo auditd EXECVE protokolliert, liefert es unabhängige Ausführungsdatensätze mit Zeitstempel, die History-Einträge bestätigen (siehe auditd).
  • Editoren, Pager und REPLs führen eigene Historien, die Angreifer selten bereinigen: siehe viminfo und lesshst.

Siehe auch