EjecuciónActividad del usuario
Historial de shell en macOS: zsh, bash y sesiones Terminal
Historial de shell de macOS en .zsh_history, .bash_history y las sesiones de Terminal por ventana: qué comandos tecleó un usuario y cuándo, aproximadamente.
- Ubicación
- ~/.zsh_history
- Prueba
- Qué comandos escribió un usuario en una shell interactiva, en qué orden y en qué ventana de Terminal
- Marcas de tiempo
- Ninguna por defecto; segundos de época Unix solo si EXTENDED_HISTORY (zsh) o HISTTIMEFORMAT (bash) está activo
- Acceso
- Propietario del archivo o root; archivos normales en la carpeta personal
- Retención
- zsh por defecto SAVEHIST=1000 líneas; los archivos de sesión de Terminal se borran tras unas dos semanas
- Adquisición
- UAC, Aftermath, mac_apt
Herramientas
Comparar todas las herramientas- mac_aptCLI · código abierto
- grepCLI · incluido en macOS
- sedCLI · incluido en macOS
Qué es
Las shells interactivas escriben los comandos que teclea un usuario en un archivo de historial de texto plano dentro de su carpeta personal. Apple convirtió zsh en la shell de inicio de sesión e interactiva por defecto para las cuentas nuevas a partir de macOS Catalina, de modo que ~/.zsh_history es el archivo principal en los sistemas actuales. Las cuentas creadas en Mojave o antes y actualizadas después pueden seguir usando bash (~/.bash_history), y los usuarios pueden cambiar de shell con chsh en cualquier momento.
El Terminal de Apple añade una segunda capa. Los scripts de inicio que proporciona Apple, /etc/zshrc_Apple_Terminal y /etc/bashrc_Apple_Terminal, implementan la función "Reanudar": cada ventana o pestaña de Terminal recibe un TERM_SESSION_ID, y sus comandos se guardan también en un archivo de historial por sesión en ~/.zsh_sessions o ~/.bash_sessions. Esto permite atribuir comandos a una ventana de Terminal concreta y, a veces, recuperar comandos recortados del archivo principal.
Dónde se encuentra
| Archivo | Ruta | Notas |
|---|---|---|
| Historial de zsh | ~/.zsh_history | Definido por HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history en /etc/zshrc; zsh por defecto desde 10.15 Catalina para cuentas nuevas |
| Sesiones de Terminal con zsh | ~/.zsh_sessions/<TERM_SESSION_ID>.history, .historynew, .session | Creados por /etc/zshrc_Apple_Terminal (carpeta con modo 700) |
| Historial de bash | ~/.bash_history | Por defecto en cuentas de 10.14 y anteriores; macOS incluye bash 3.2 |
| Sesiones de Terminal con bash | ~/.bash_sessions/<TERM_SESSION_ID>.history, .historynew, .session | Creados por /etc/bashrc_Apple_Terminal |
| Historial de root | /private/var/root/.zsh_history (o .bash_history) | Comandos tecleados en una shell de root, como sudo -i |
| Archivos de inicio | /etc/zshrc, /etc/zprofile, ~/.zshrc, ~/.zprofile, ~/.zshenv, ~/.zlogin | Busca un HISTFILE, HISTSIZE o SAVEHIST modificado o un historial desactivado |
Si ZDOTDIR está definido, zsh busca sus archivos en ese directorio en lugar de $HOME. Otras shells (fish, ksh, tcsh) mantienen sus propios archivos de historial. Las carpetas personales están en el volumen Data (/System/Volumes/Data/Users/<name>). Los dotfiles no están en una ubicación protegida por TCC, pero la herramienta de adquisición debería ejecutarse igualmente con Full Disk Access.
Qué prueba
- Los comandos tecleados en shells interactivas, en el orden en que se guardaron.
- Patrones de descarga y ejecución (
curl ... | sh,chmod +x,xattr -d com.apple.quarantine), configuración de persistencia (launchctl load, edición de LaunchAgents), reconocimiento, creación de archivos comprimidos y comandos de exfiltración. - Con los archivos de sesión de Terminal: qué comandos pertenecen a qué ventana de Terminal y cuándo se guardó una sesión por última vez.
No prueba:
- Que un comando tuviera éxito, ni lo que mostró.
- Cuándo se ejecutó un comando, salvo que estén activadas las marcas de tiempo extendidas.
- Nada sobre la ejecución no interactiva: los scripts,
osascript, los trabajos de launchd y las herramientas remotas que lanzansh -cno escriben historial. Para eso, usa los Unified Logs y la telemetría de procesos.
Campos clave
Formato por defecto de zsh: un comando por línea, sin marcas de tiempo.
zsh con setopt EXTENDED_HISTORY:
: 1790000000:4;curl -fsSL https://example.test/a.sh -o /tmp/a.sh
| Parte | Significado |
|---|---|
: | Marcador (zsh escribe un comando : que no hace nada) |
1790000000 | Hora de inicio del comando, segundos de época Unix |
4 | Tiempo transcurrido en segundos |
tras ; | La línea de comandos |
bash con HISTTIMEFORMAT definido escribe una línea de comentario #<epoch> antes de cada comando. Los archivos .session de Terminal contienen una línea que muestra Restored session: seguido de date -r <epoch>, donde la época es la hora a la que se guardó el estado de la sesión.
Marcas de tiempo
- Configuración por defecto: no hay ninguna hora por comando. Solo ayudan las horas del sistema de archivos del archivo de historial: su hora de modificación indica aproximadamente cuándo una shell salió y guardó el historial por última vez (consulta marcas de tiempo APFS).
EXTENDED_HISTORYde zsh yHISTTIMEFORMATde bash guardan segundos de época Unix, UTC. Ninguno está activado en los/etc/zshrco/etc/bashrcpor defecto de Apple.- Archivos de sesión de Terminal: la época del
.session, más las horas de modificación de cada archivo.history, dan una hora de "último guardado" por ventana.
# 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
Retención
- El
/etc/zshrcde Apple defineHISTSIZE=2000(en memoria) ySAVEHIST=1000(líneas que se conservan en el archivo). Las líneas más antiguas se recortan a medida que se guardan otras nuevas. - zsh añade el historial al salir la shell (
APPEND_HISTORYestá activo por defecto). Una ventana cerrada a la fuerza, o un fallo, puede perder los comandos de esa sesión. Opciones comoINC_APPEND_HISTORYoSHARE_HISTORYescriben cada comando inmediatamente. - La lógica de sesiones de Terminal borra los archivos de
~/.zsh_sessions/~/.bash_sessionscon más de dos semanas, comprobándolo como máximo una vez al día (_expiration_check_timestamp). - Los usuarios pueden desactivar o borrar el historial:
unset HISTFILE,HISTSIZE=0,SHELL_SESSIONS_DISABLE=1(solo desactiva los archivos de sesión de Terminal, no el historial), un espacio inicial conHIST_IGNORE_SPACE,history -cen bash o, simplemente, borrar el archivo.
Adquisición
# 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.yamlybash.yamlrecopilan los archivos de historial, las carpetas de sesión, los archivos de inicio y cualquierHISTFILEpersonalizado que aparezca en ellos. - Aftermath: recopila el historial de bash y zsh,
.sh_historyy los archivos de inicio del shell, incluidoconfig.fishde fish; no recoge historial de csh, ksh ni fish (root más Full Disk Access). - mac_apt: el plugin
TERMSESSIONSlee las carpetas de sesión y el historial de bash y zsh de todos los usuarios.
Si es posible, adquiere en vivo antes de que el usuario cierre sesión: las shells abiertas guardan en memoria los comandos aún no escritos.
Análisis
Los archivos de historial son texto, así que cat, grep y sed hacen la mayor parte del trabajo. Aspectos a vigilar:
- zsh escribe el archivo en su codificación interna "metafied": los bytes del rango meta se guardan como
0x83seguido del byte con XOR0x20. Los comandos con caracteres acentuados o no latinos aparecen corruptos hasta que se revierte (elimina0x83y aplica XOR0x20al byte siguiente). - El plugin
TERMSESSIONSde mac_apt fusiona los archivos.historyy.historynewde cada sesión porTERM_SESSION_ID. - Triaje rápido de comandos habituales de atacantes:
grep -nE 'curl|wget|base64|osascript|xattr|launchctl|chmod \+x|nc |python3? -c|/tmp/|/Users/Shared/' .zsh_history
Consejos para la investigación
- Compara
.zsh_historycon cada~/.zsh_sessions/*.history: los archivos de sesión se recortan por separado y pueden contener comandos que faltan en el archivo principal. - Un archivo de historial vacío o de cero bytes con una hora de modificación reciente, o un enlace simbólico a
/dev/null, es una señal antiforense clásica. Buscaunset HISTFILEoSAVEHIST=0en los archivos de inicio. - Las shells de root escriben en
/private/var/root; combínalas con los logs de sudo para ver quién elevó privilegios. - Los comandos que tocan LaunchAgents o LaunchDaemons deben contrastarse con los plists de launchd y con FSEvents para localizar la creación del archivo correspondiente.
- Los snapshots locales de APFS pueden contener versiones más antiguas y más largas del archivo de historial.
/etc/zshrcsolo carga la lógica de sesiones cuandoTERM_PROGRAMvaleApple_Terminal, así que iTerm2, los terminales de VS Code y emuladores similares solo escriben en el archivo de historial principal, nunca en~/.zsh_sessions.