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
- mac_aptCLI · Open Source
- grepCLI · in macOS enthalten
- sedCLI · in macOS enthalten
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
| Datei | Pfad | Hinweise |
|---|---|---|
| zsh-Historie | ~/.zsh_history | Gesetzt 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, .session | Angelegt von /etc/zshrc_Apple_Terminal (Ordnermodus 700) |
| bash-Historie | ~/.bash_history | Standard 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, .session | Angelegt 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, ~/.zlogin | Auf 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, diesh -cstarten, 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
| Teil | Bedeutung |
|---|---|
: | Markierung (zsh schreibt einen wirkungslosen Befehl :) |
1790000000 | Startzeit des Befehls, Sekunden der Unix-Epoche |
4 | Laufzeit 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_HISTORYund bashHISTTIMEFORMATspeichern Sekunden der Unix-Epoche, UTC. Keines von beiden ist in Apples Standard-/etc/zshrcoder/etc/bashrcaktiviert. - Sitzungsdateien des Terminals: Die Epoche in
.sessionsowie 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/zshrcsetztHISTSIZE=2000(im Speicher) undSAVEHIST=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_HISTORYist standardmäßig aktiv). Ein hart beendetes Fenster oder ein Absturz kann die Befehle dieser Sitzung verlieren. Optionen wieINC_APPEND_HISTORYoderSHARE_HISTORYschreiben 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 mitHIST_IGNORE_SPACE,history -cin 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.yamlundbash.yamlsichern Historie-Dateien, Sitzungsordner, Startdateien und jedes darin gefundene benutzerdefinierteHISTFILE. - Aftermath: sichert die Historie von bash und zsh,
.sh_historyund Shell-Startdateien einschließlichconfig.fishvon fish; keine Historie von csh, ksh oder fish (root plus Full Disk Access). - mac_apt: Das Plugin
TERMSESSIONSliest 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
0x83gespeichert, gefolgt vom Byte XOR0x20. Befehle mit Akzenten oder nicht lateinischen Zeichen wirken verstümmelt, bis Sie das umkehren (0x83entfernen, das folgende Byte mit0x20XOR-verknüpfen). - mac_apt
TERMSESSIONSführt die Sitzungsdateien.historyund.historynewproTERM_SESSION_IDzusammen. - 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_historymit 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/nullist ein klassisches Zeichen für Anti-Forensik. Prüfen Sie die Startdateien aufunset HISTFILEoderSAVEHIST=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/zshrclädt die Sitzungslogik nur, wennTERM_PROGRAMden WertApple_Terminalhat. iTerm2, Terminals in VS Code und ähnliche Emulatoren schreiben daher nur in die Hauptdatei, nie nach~/.zsh_sessions.