Aller au contenu

ExécutionActivité utilisateur

Historique du shell sous macOS : zsh, bash et Terminal

L'historique du shell macOS dans .zsh_history, .bash_history et les fichiers de session du Terminal : quelles commandes ont été tapées et vers quand.

Emplacement
~/.zsh_history
Prouve
Quelles commandes un utilisateur a tapées dans un shell interactif, dans quel ordre, et dans quelle fenêtre du Terminal
Horodatages
Aucun par défaut ; secondes d'époque Unix uniquement si EXTENDED_HISTORY (zsh) ou HISTTIMEFORMAT (bash) est défini
Accès
Propriétaire du fichier ou root ; fichiers texte dans le dossier personnel
Rétention
zsh : SAVEHIST=1000 lignes par défaut ; fichiers de session du Terminal supprimés après environ deux semaines
Collecte
UAC, Aftermath, mac_apt

Ce que c'est

Les shells interactifs écrivent les commandes tapées par l'utilisateur dans un fichier d'historique en texte brut, dans le dossier personnel. Apple a fait de zsh le shell de connexion et interactif par défaut des nouveaux comptes à partir de macOS Catalina : ~/.zsh_history est donc le fichier principal sur les systèmes actuels. Les comptes créés sous Mojave ou avant puis mis à niveau peuvent encore utiliser bash (~/.bash_history), et les utilisateurs peuvent changer de shell à tout moment avec chsh.

Le Terminal d'Apple ajoute une seconde couche. Les scripts de démarrage fournis par Apple, /etc/zshrc_Apple_Terminal et /etc/bashrc_Apple_Terminal, implémentent la fonction « Reprise » : chaque fenêtre ou onglet du Terminal reçoit un TERM_SESSION_ID, et ses commandes sont aussi enregistrées dans un fichier d'historique par session dans ~/.zsh_sessions ou ~/.bash_sessions. Cela permet d'attribuer des commandes à une fenêtre précise du Terminal, et parfois de récupérer des commandes élaguées du fichier principal.

Où il se trouve

FichierCheminRemarques
Historique zsh~/.zsh_historyDéfini par HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history dans /etc/zshrc ; zsh par défaut depuis 10.15 Catalina pour les nouveaux comptes
Sessions Terminal zsh~/.zsh_sessions/<TERM_SESSION_ID>.history, .historynew, .sessionCréés par /etc/zshrc_Apple_Terminal (dossier en mode 700)
Historique bash~/.bash_historyPar défaut pour les comptes créés sous 10.14 et avant ; macOS fournit bash 3.2
Sessions Terminal bash~/.bash_sessions/<TERM_SESSION_ID>.history, .historynew, .sessionCréés par /etc/bashrc_Apple_Terminal
Historique de root/private/var/root/.zsh_history (ou .bash_history)Commandes tapées dans un shell de connexion root, comme sudo -i
Fichiers de démarrage/etc/zshrc, /etc/zprofile, ~/.zshrc, ~/.zprofile, ~/.zshenv, ~/.zloginCherchez un HISTFILE, HISTSIZE ou SAVEHIST modifié, ou un historique désactivé

Si ZDOTDIR est défini, zsh cherche ses fichiers dans ce répertoire au lieu de $HOME. Les autres shells (fish, ksh, tcsh) ont leurs propres fichiers d'historique. Les dossiers personnels se trouvent sur le volume Data (/System/Volumes/Data/Users/<name>). Les dotfiles eux-mêmes ne sont pas dans un emplacement protégé par TCC, mais l'outil de collecte devrait tout de même disposer de l'accès complet au disque.

Ce qu'il prouve

  • Les commandes tapées dans des shells interactifs, dans l'ordre de leur enregistrement.
  • Des schémas de téléchargement et exécution (curl ... | sh, chmod +x, xattr -d com.apple.quarantine), la mise en place d'une persistance (launchctl load, modification de LaunchAgents), de la reconnaissance, la création d'archives et des commandes d'exfiltration.
  • Avec les fichiers de session du Terminal : quelles commandes appartiennent à quelle fenêtre, et quand une session a été enregistrée pour la dernière fois.

Il ne prouve pas :

  • Qu'une commande a réussi, ni ce qu'elle a affiché.
  • Quand une commande s'est exécutée, sauf si les horodatages étendus sont activés.
  • Quoi que ce soit sur l'exécution non interactive : les scripts, osascript, les tâches launchd et les outils distants qui lancent sh -c n'écrivent pas d'historique. Utilisez pour cela les Unified Logs et la télémétrie des processus.

Champs clés

Format zsh par défaut : une commande par ligne, sans horodatage.

zsh avec setopt EXTENDED_HISTORY :

: 1790000000:4;curl -fsSL https://example.test/a.sh -o /tmp/a.sh
PartieSignification
: Marqueur (zsh écrit une commande : sans effet)
1790000000Heure de début de la commande, secondes d'époque Unix
4Durée écoulée en secondes
après ;La ligne de commande

bash avec HISTTIMEFORMAT défini écrit une ligne de commentaire #<epoch> avant chaque commande. Les fichiers .session du Terminal contiennent une ligne qui affiche Restored session: suivi de date -r <epoch>, où l'époque correspond au moment où l'état de la session a été enregistré.

Horodatages

  • Configuration par défaut : aucune heure par commande. Seules les dates du fichier d'historique dans le système de fichiers aident : sa date de modification correspond approximativement à la dernière fois qu'un shell s'est terminé et a enregistré l'historique (voir horodatages APFS).
  • EXTENDED_HISTORY de zsh et HISTTIMEFORMAT de bash stockent des secondes d'époque Unix, en UTC. Aucun des deux n'est activé dans les /etc/zshrc ou /etc/bashrc par défaut d'Apple.
  • Fichiers de session du Terminal : l'époque contenue dans .session, plus les dates de modification de chaque fichier .history, donnent une heure de « dernier enregistrement » par fenêtre.
# 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

Rétention

  • Le /etc/zshrc d'Apple définit HISTSIZE=2000 (en mémoire) et SAVEHIST=1000 (lignes conservées dans le fichier). Les lignes les plus anciennes sont élaguées à mesure que de nouvelles sont enregistrées.
  • zsh ajoute l'historique au fichier à la fermeture du shell (APPEND_HISTORY est actif par défaut). Une fenêtre tuée brutalement, ou un plantage, peut faire perdre les commandes de cette session. Des options comme INC_APPEND_HISTORY ou SHARE_HISTORY écrivent chaque commande immédiatement.
  • La logique de session du Terminal supprime les fichiers de ~/.zsh_sessions / ~/.bash_sessions de plus de deux semaines, en vérifiant au plus une fois par jour (_expiration_check_timestamp).
  • Les utilisateurs peuvent désactiver ou effacer l'historique : unset HISTFILE, HISTSIZE=0, SHELL_SESSIONS_DISABLE=1 (désactive seulement les fichiers de session de Terminal, pas l'historique), une espace en début de ligne avec HIST_IGNORE_SPACE, history -c sous bash, ou simplement la suppression du fichier.

Collecte

# 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 et bash.yaml collectent les fichiers d'historique, les dossiers de session, les fichiers de démarrage et tout HISTFILE personnalisé qui y figure.
  • Aftermath : collecte l'historique de bash et de zsh, .sh_history et les fichiers de démarrage du shell, dont config.fish pour fish ; aucun historique csh, ksh ou fish (root plus accès complet au disque).
  • mac_apt : le plugin TERMSESSIONS lit les dossiers de session et l'historique bash et zsh de chaque utilisateur.

Si possible, collectez en live avant que l'utilisateur ne se déconnecte : les shells ouverts ne conservent les commandes non enregistrées qu'en mémoire.

Analyse

Les fichiers d'historique sont du texte : cat, grep et sed font l'essentiel du travail. Points d'attention :

  • zsh écrit le fichier dans son encodage interne « metafied » : les octets de la plage meta sont stockés sous la forme 0x83 suivi de l'octet XOR 0x20. Les commandes contenant des caractères accentués ou non latins semblent corrompues tant que l'on n'inverse pas cette transformation (supprimer 0x83, appliquer un XOR 0x20 à l'octet suivant).
  • Le plugin TERMSESSIONS de mac_apt fusionne les fichiers de session .history et .historynew par TERM_SESSION_ID.
  • Tri rapide des commandes d'attaquant courantes :
grep -nE 'curl|wget|base64|osascript|xattr|launchctl|chmod \+x|nc |python3? -c|/tmp/|/Users/Shared/' .zsh_history

Conseils d'investigation

  • Comparez .zsh_history avec chaque ~/.zsh_sessions/*.history : les fichiers de session sont élagués indépendamment et peuvent contenir des commandes absentes du fichier principal.
  • Un fichier d'historique vide ou de taille nulle avec une date de modification récente, ou un lien symbolique vers /dev/null, est un signe classique d'anti-forensique. Cherchez unset HISTFILE ou SAVEHIST=0 dans les fichiers de démarrage.
  • Les shells root écrivent dans /private/var/root ; associez-les aux journaux sudo pour savoir qui a élevé ses privilèges.
  • Les commandes qui touchent LaunchAgents ou LaunchDaemons doivent être recoupées avec les plists launchd et FSEvents pour retrouver la création du fichier correspondant.
  • Les snapshots APFS locaux peuvent contenir des versions plus anciennes et plus longues du fichier d'historique.
  • /etc/zshrc ne charge la logique de session que lorsque TERM_PROGRAM vaut Apple_Terminal : iTerm2, les terminaux de VS Code et les émulateurs similaires n'écrivent que dans le fichier d'historique principal, jamais dans ~/.zsh_sessions.

Voir aussi