ExécutionActivité utilisateurAnti-forensique
Historique du shell : commandes bash, zsh et fish
Historique des commandes par utilisateur de bash, zsh et fish : contenu de chaque fichier, présence des horodatages et traces d'évasion de l'historique.
- Emplacement
- ~/.bash_history
- Prouve
- Quelles commandes ont été saisies dans un shell interactif sous un compte donné, et parfois quand
- Horodatages
- Secondes epoch Unix (UTC) si enregistrées : bash seulement avec HISTTIMEFORMAT, zsh avec EXTENDED_HISTORY, fish toujours
- Accès
- Tout utilisateur pour ses propres fichiers ; root pour les répertoires personnels des autres
- Rétention
- Jusqu'à troncature par HISTFILESIZE/SAVEHIST ou suppression
- Collecte
- UAC, Velociraptor, cp -a, tar
Outils
Comparer tous les outilsCe que c'est
Les shells interactifs conservent la liste des lignes de commande saisies par l'utilisateur et l'enregistrent dans un fichier propre à chaque utilisateur afin de pouvoir les rappeler lors de sessions ultérieures. Bash, zsh et fish utilisent chacun leur propre fichier et leur propre format. Ces fichiers sont en texte brut (fish utilise une structure proche du YAML), appartiennent à l'utilisateur qui peut y écrire, et sont écrits au rythme du shell et non à chaque exécution de commande.
Pour un enquêteur, c'est souvent la trace la plus directe de l'activité manuelle après la compromission d'un compte. Il est aussi incomplet par nature : ce que le shell n'a jamais écrit sur disque, ou ce que l'utilisateur a choisi de supprimer, n'y figure tout simplement pas.
Où il se trouve
| Shell | Fichier par défaut | Remarques |
|---|---|---|
| bash | ~/.bash_history | Chemin défini par HISTFILE ; si HISTFILE est non défini ou vide, rien n'est enregistré à la sortie |
| bash (root) | /root/.bash_history | Là où arrivent les commandes après sudo -i, sudo su - ou su - |
| zsh | Pas de valeur par défaut ; souvent ~/.zsh_history ou ~/.zhistory | Écrit uniquement si HISTFILE est défini (et SAVEHIST non nul) dans un fichier de démarrage |
| fish | ~/.local/share/fish/fish_history | $XDG_DATA_HOME/fish/ s'il est défini ; la variable fish_history change le nom de session en <name>_history, une valeur vide désactive l'enregistrement |
Les chemins sont identiques sur Debian/Ubuntu, RHEL/Fedora et les autres familles. Ce qui diffère, c'est la configuration par défaut : par exemple, le ~/.bashrc du squelette Debian et Ubuntu définit HISTCONTROL=ignoreboth et active histappend, alors que d'autres distributions peuvent définir les variables d'historique dans /etc/profile, /etc/bashrc ou /etc/profile.d/. Lisez toujours les fichiers de démarrage réels de l'image (voir fichiers de démarrage du shell) avant d'interpréter des lacunes.
Les comptes de service ont aussi des répertoires personnels : vérifiez /var/www, /var/lib/postgresql, /var/lib/mysql, /opt/* et tout répertoire listé dans /etc/passwd à la recherche de fichiers d'historique égarés.
Ce qu'il prouve
- Quelles lignes de commande ont été saisies dans un shell interactif tournant sous ce compte.
- L'ordre relatif dans lequel un shell a écrit ces lignes (pas toujours l'ordre d'exécution entre sessions parallèles).
- Avec horodatages : quand chaque commande a été saisie (zsh et fish permettent aussi de borner l'activité d'une session).
- L'usage d'outils, les chemins de fichiers, les hôtes distants, les URL et les identifiants passés en arguments, qui mènent vers d'autres artefacts.
Il ne prouve pas :
- Qui était au clavier (les comptes partagés, volés ou changés via
sucassent l'attribution). - Qu'une commande a réussi, ni même qu'elle a été exécutée si elle a été saisie puis annulée.
- Quoi que ce soit sur l'exécution non interactive : scripts, tâches cron, web shells et
ssh host 'cmd'ne laissent normalement rien ici.
Champs clés
| Format | Structure de l'enregistrement | Signification |
|---|---|---|
| bash, simple | command line | Une entrée par ligne, sans heure |
bash, HISTTIMEFORMAT défini | #1727512345 puis command line | Le caractère de commentaire suivi de chiffres marque l'horodatage de l'entrée suivante ; il délimite aussi les entrées multilignes |
zsh EXTENDED_HISTORY | : <start>:<elapsed>;command | Heure de début en secondes epoch, durée écoulée en secondes, puis la commande |
| fish | - cmd: ... / when: ... / liste paths: facultative | when est en secondes epoch ; paths liste les arguments que fish a reconnus comme des chemins existants |
Zsh stocke les octets non ASCII dans un encodage « métafié » : certains caractères paraissent corrompus dans une visionneuse texte classique.
Horodatages
Les trois formats utilisent des secondes epoch Unix, donc en UTC. Le mtime du fichier indique la dernière écriture par un shell, et le ctime le dernier changement de métadonnées (utile quand un fichier a été tronqué ou remplacé par un lien symbolique).
# 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
Rétention
- bash : à la sortie, les
HISTSIZEdernières entrées (500 par défaut) sont ajoutées au fichier (avechistappend) ou l'écrasent, puis le fichier est réduit àHISTFILESIZElignes. L'activité ancienne disparaît par le haut. - zsh :
SAVEHISTplafonne le fichier.APPEND_HISTORYest activé par défaut ;INC_APPEND_HISTORYouSHARE_HISTORYécrivent chaque commande immédiatement au lieu d'attendre la sortie. - fish :
history clearethistory deletesuppriment des entrées ;history mergeintègre les modifications des autres sessions. - Les sessions tuées par
SIGKILL, plantées ou encore ouvertes au moment de l'acquisition peuvent ne jamais atteindre le disque. Un bash en cours d'exécution les garde en mémoire du processus.
Collecte
Collectez depuis chaque répertoire personnel et /root, y compris les copies tournées ou de sauvegarde (.bash_history~, .bash_history.*). Les artefacts files/shell/* d'UAC (inclus dans ir_triage) couvrent bash, zsh, fish et d'autres shells, et analysent aussi les fichiers de démarrage pour trouver un HISTFILE non standard.
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
Dans Velociraptor, Linux.Sys.BashHistory recherche /{root,home/*}/.*_history sur l'ensemble des postes. Si le shell suspect est peut-être encore actif, capturez d'abord la mémoire (LiME ou AVML) : l'acquisition du disque ne vide pas l'historique en mémoire, mais une déconnexion ultérieure écrasera ce que vous avez vu.
Analyse
- Plaso : les plugins texte
bash_historyetzsh_extended_historyfont partie du presetlinux(sélectionnez-les avectext/bash_historyettext/zsh_extended_history) ;fish_historyest un parser distinct. Seules les entrées horodatées deviennent des événements. - Volatility 3 :
linux.bash.Bashrécupère l'historique des processus bash en cours, y compris les commandes jamais écrites sur disque. - Les outils simples suffisent pour la revue :
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
Conseils d'investigation
- Considérez un historique vide, de zéro octet ou lié symboliquement à
/dev/nullsur un compte actif comme une piste. Vérifiez lectimedu lien symbolique et comparez lemtimedu fichier avec les connexions dans wtmp et lastlog. - Recherchez les chaînes d'évasion dans les historiques d'autres shells, les fichiers de démarrage et la mémoire :
unset HISTFILE,HISTFILE=/dev/null,HISTSIZE=0,set +o history,history -c,history -d,kill -9 $$. - L'absence des commandes saisies avec un espace initial est normale là où
ignorespace/ignorebothouHIST_IGNORE_SPACEde zsh est défini, et fish écarte ces lignes par conception. C'est un réglage par défaut, pas une preuve d'intention. - Une couverture
#epochpartielle dans un fichier bash signifie queHISTTIMEFORMATa été activé à un moment donné ; les lignes sans horodatage sont plus anciennes ou proviennent d'un shell où il n'était pas défini. - Après
sudo -iousu -, poursuivez dans/root/.bash_historyet corrélez avec les journaux sudo. - Quand auditd journalise
EXECVE, il fournit des traces d'exécution indépendantes et horodatées pour confirmer les entrées d'historique (voir auditd). - Les éditeurs, pagers et REPL tiennent leurs propres historiques, que les attaquants nettoient rarement : voir viminfo et lesshst.
Voir aussi
Artefacts liés
Guides détaillés
Glossaire
- Volatility 3Anglais
- Super TimelineAnglais
- UAC (Unix-like Artifacts Collector)Anglais