Zum Inhalt springen

AusführungBenutzeraktivität

Shell-Historie unter macOS: zsh, bash und Terminal-Sitzungen

Shell-Historie in .zsh_history, .bash_history und den Sitzungsdateien des Terminals pro Fenster: welche Befehle ein Benutzer eingab und ungefähr wann.

Speicherort
~/.zsh_history
Belegt
Welche Befehle ein Benutzer in einer interaktiven Shell eingegeben hat, in welcher Reihenfolge und in welchem Terminal-Fenster
Zeitstempel
Standardmäßig keine; Sekunden der Unix-Epoche nur, wenn EXTENDED_HISTORY (zsh) oder HISTTIMEFORMAT (bash) gesetzt ist
Zugriff
Dateibesitzer oder root; einfache Dateien im Home-Ordner
Aufbewahrung
zsh-Standard SAVEHIST=1000 Zeilen; Sitzungsdateien des Terminals werden nach etwa zwei Wochen gelöscht
Sicherung
UAC, Aftermath, mac_apt

Was es ist

Interaktive Shells schreiben die vom Benutzer eingegebenen Befehle in eine Textdatei im Home-Ordner. Apple hat zsh ab macOS Catalina zur Standard-Login- und interaktiven Shell für neue Konten gemacht, daher ist ~/.zsh_history auf aktuellen Systemen die Hauptdatei. Konten, die unter Mojave oder früher angelegt und später aktualisiert wurden, können noch bash nutzen (~/.bash_history), und Benutzer können die Shell jederzeit mit chsh wechseln.

Apples Terminal fügt eine zweite Ebene hinzu. Die von Apple mitgelieferten Startskripte /etc/zshrc_Apple_Terminal und /etc/bashrc_Apple_Terminal setzen „Resume“ um: Jedes Terminal-Fenster oder jeder Tab erhält eine TERM_SESSION_ID, und seine Befehle werden zusätzlich in einer Historie-Datei pro Sitzung in ~/.zsh_sessions oder ~/.bash_sessions gespeichert. So lassen sich Befehle einem bestimmten Terminal-Fenster zuordnen, und manchmal werden Befehle zurückgewonnen, die aus der Hauptdatei gekürzt wurden.

Wo es liegt

DateiPfadHinweise
zsh-Historie~/.zsh_historyGesetzt durch HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history in /etc/zshrc; zsh ist seit 10.15 Catalina Standard für neue Konten
zsh-Terminal-Sitzungen~/.zsh_sessions/<TERM_SESSION_ID>.history, .historynew, .sessionAngelegt von /etc/zshrc_Apple_Terminal (Ordnermodus 700)
bash-Historie~/.bash_historyStandard für Konten unter 10.14 und früher; macOS liefert bash 3.2 aus
bash-Terminal-Sitzungen~/.bash_sessions/<TERM_SESSION_ID>.history, .historynew, .sessionAngelegt von /etc/bashrc_Apple_Terminal
Historie von root/private/var/root/.zsh_history (oder .bash_history)Befehle aus einer root-Login-Shell wie sudo -i
Startdateien/etc/zshrc, /etc/zprofile, ~/.zshrc, ~/.zprofile, ~/.zshenv, ~/.zloginAuf geändertes HISTFILE, HISTSIZE, SAVEHIST oder deaktivierte Historie prüfen

Ist ZDOTDIR gesetzt, sucht zsh seine Dateien in diesem Verzeichnis statt in $HOME. Andere Shells (fish, ksh, tcsh) führen eigene Historie-Dateien. Home-Ordner liegen auf dem Data-Volume (/System/Volumes/Data/Users/<name>). Die Dotfiles selbst liegen nicht an einem TCC-geschützten Ort, ein Sicherungstool sollte aber dennoch mit Full Disk Access laufen.

Was es belegt

  • Die in interaktiven Shells eingegebenen Befehle, in der Reihenfolge ihrer Speicherung.
  • Muster wie Herunterladen und Ausführen (curl ... | sh, chmod +x, xattr -d com.apple.quarantine), das Einrichten von Persistenz (launchctl load, Bearbeiten von LaunchAgents), Aufklärung, Erstellen von Archiven und Befehle zur Exfiltration.
  • Mit Sitzungsdateien des Terminals: welche Befehle zu welchem Terminal-Fenster gehören und wann eine Sitzung zuletzt gespeichert wurde.

Es belegt nicht:

  • Dass ein Befehl erfolgreich war oder was er ausgegeben hat.
  • Wann ein Befehl lief, sofern keine erweiterten Zeitstempel aktiviert sind.
  • Irgendetwas über nicht interaktive Ausführung: Skripte, osascript, launchd-Jobs und Remote-Tools, die sh -c starten, schreiben keine Historie. Nutzen Sie dafür die Unified Logs und Prozess-Telemetrie.

Wichtige Felder

Standardformat von zsh: ein Befehl pro Zeile, keine Zeitstempel.

zsh mit setopt EXTENDED_HISTORY:

: 1790000000:4;curl -fsSL https://example.test/a.sh -o /tmp/a.sh
TeilBedeutung
: Markierung (zsh schreibt einen wirkungslosen Befehl :)
1790000000Startzeit des Befehls, Sekunden der Unix-Epoche
4Laufzeit in Sekunden
nach ;Die Befehlszeile

bash mit gesetztem HISTTIMEFORMAT schreibt vor jeden Befehl eine Kommentarzeile #<epoch>. Die .session-Dateien des Terminals enthalten eine Zeile, die Restored session: ausgibt, gefolgt von date -r <epoch>, wobei die Epoche der Zeitpunkt ist, zu dem der Sitzungszustand gespeichert wurde.

Zeitstempel

  • Standardkonfiguration: überhaupt keine Zeit pro Befehl. Nur die Dateisystemzeiten der Historie-Datei helfen: Ihre Änderungszeit entspricht ungefähr dem Zeitpunkt, zu dem eine Shell zuletzt beendet wurde und die Historie speicherte (siehe APFS-Zeitstempel).
  • zsh EXTENDED_HISTORY und bash HISTTIMEFORMAT speichern Sekunden der Unix-Epoche, UTC. Keines von beiden ist in Apples Standard-/etc/zshrc oder /etc/bashrc aktiviert.
  • Sitzungsdateien des Terminals: Die Epoche in .session sowie die Änderungszeiten jeder .history-Datei liefern pro Fenster einen Zeitpunkt „zuletzt gespeichert“.
# Convert zsh extended history to readable UTC
sed -nE 's/^: ([0-9]+):([0-9]+);(.*)$/\1 \2 \3/p' .zsh_history |
while read -r ts dur cmd; do printf '%s  %ss  %s\n' "$(date -u -r "$ts" '+%F %T')" "$dur" "$cmd"; done

Aufbewahrung

  • Apples /etc/zshrc setzt HISTSIZE=2000 (im Speicher) und SAVEHIST=1000 (in der Datei behaltene Zeilen). Ältere Zeilen werden gekürzt, wenn neue gespeichert werden.
  • zsh hängt die Historie beim Beenden der Shell an (APPEND_HISTORY ist standardmäßig aktiv). Ein hart beendetes Fenster oder ein Absturz kann die Befehle dieser Sitzung verlieren. Optionen wie INC_APPEND_HISTORY oder SHARE_HISTORY schreiben jeden Befehl sofort.
  • Die Sitzungslogik des Terminals löscht Dateien in ~/.zsh_sessions / ~/.bash_sessions, die älter als zwei Wochen sind, und prüft höchstens einmal am Tag (_expiration_check_timestamp).
  • Benutzer können die Historie deaktivieren oder löschen: unset HISTFILE, HISTSIZE=0, SHELL_SESSIONS_DISABLE=1 (schaltet nur die Sitzungsdateien von Terminal ab, nicht die Historie), ein führendes Leerzeichen mit HIST_IGNORE_SPACE, history -c in bash oder schlicht das Löschen der Datei.

Sicherung

# Per user, preserving timestamps (run with Full Disk Access)
for h in /Users/*; do
  ditto "$h/.zsh_history" "/cases/host01/$(basename "$h")/zsh_history" 2>/dev/null
  ditto "$h/.zsh_sessions" "/cases/host01/$(basename "$h")/zsh_sessions" 2>/dev/null
  ditto "$h/.bash_history" "/cases/host01/$(basename "$h")/bash_history" 2>/dev/null
  ditto "$h/.bash_sessions" "/cases/host01/$(basename "$h")/bash_sessions" 2>/dev/null
done
sudo ditto /private/var/root /cases/host01/root_home
  • UAC: zsh.yaml und bash.yaml sichern Historie-Dateien, Sitzungsordner, Startdateien und jedes darin gefundene benutzerdefinierte HISTFILE.
  • Aftermath: sichert die Historie von bash und zsh, .sh_history und Shell-Startdateien einschließlich config.fish von fish; keine Historie von csh, ksh oder fish (root plus Full Disk Access).
  • mac_apt: Das Plugin TERMSESSIONS liest die Sitzungsordner und die Historie von bash und zsh für jeden Benutzer.

Sichern Sie nach Möglichkeit live, bevor sich ein Benutzer abmeldet: Offene Shells halten ungespeicherte Befehle nur im Speicher.

Auswertung

Historie-Dateien sind Text, daher erledigen cat, grep und sed den Großteil der Arbeit. Worauf zu achten ist:

  • zsh schreibt die Datei in seiner internen „metafied“-Kodierung: Bytes im Meta-Bereich werden als 0x83 gespeichert, gefolgt vom Byte XOR 0x20. Befehle mit Akzenten oder nicht lateinischen Zeichen wirken verstümmelt, bis Sie das umkehren (0x83 entfernen, das folgende Byte mit 0x20 XOR-verknüpfen).
  • mac_apt TERMSESSIONS führt die Sitzungsdateien .history und .historynew pro TERM_SESSION_ID zusammen.
  • Schnelle Sichtung auf typische Befehle von Angreifern:
grep -nE 'curl|wget|base64|osascript|xattr|launchctl|chmod \+x|nc |python3? -c|/tmp/|/Users/Shared/' .zsh_history

Tipps für die Untersuchung

  • Vergleichen Sie .zsh_history mit jeder ~/.zsh_sessions/*.history: Sitzungsdateien werden unabhängig gekürzt und können Befehle enthalten, die in der Hauptdatei fehlen.
  • Eine leere Historie-Datei oder eine mit null Byte und jüngerer Änderungszeit oder ein Symlink auf /dev/null ist ein klassisches Zeichen für Anti-Forensik. Prüfen Sie die Startdateien auf unset HISTFILE oder SAVEHIST=0.
  • root-Shells schreiben nach /private/var/root; kombinieren Sie sie mit den sudo-Logs, um zu sehen, wer Rechte erlangt hat.
  • Befehle, die LaunchAgents oder LaunchDaemons berühren, sollten mit den launchd-Plists und FSEvents auf die passende Dateierstellung abgeglichen werden.
  • Lokale APFS-Snapshots können ältere, längere Versionen der Historie-Datei enthalten.
  • /etc/zshrc lädt die Sitzungslogik nur, wenn TERM_PROGRAM den Wert Apple_Terminal hat. iTerm2, Terminals in VS Code und ähnliche Emulatoren schreiben daher nur in die Hauptdatei, nie nach ~/.zsh_sessions.

Siehe auch