Today I Learned - Zsh Sessions (even more Timestamps)
A short DFIR technical note explaining zsh's ~/.zsh_sessions directory, where each terminal session gets a paired .session/.history file (or a live .historynew tracked by $HISTFILE) keyed by TERM_SESSION_ID. The .session file contains a restored-session timestamp, giving investigators a way to recover executed commands and narrow down execution timeframes even when an attacker has deleted the main .zsh_history file.
What it is
A DFIR technique for recovering shell command history and timestamps from Zsh's .zsh_sessions directory on macOS/Linux, the successor artifact to fc -lf/fc -li live-response methods. The value is that these files survive deletion of .zsh_history.
How it works
.zsh_sessions sits at the root of the user's home directory. Inside, each terminal session produces two files sharing one name — the TERM_SESSION_ID (a UUID generated at session start, constant for its lifetime):
<UUID>.history— the commands run in that session (each pairs with a.sessionfile of the same UUID).<UUID>.session— a one-line shell command containing the reopen timestamp.
The currently active session instead has a single <UUID>.historynew file with no .session companion; $HISTFILE points to it. The .session file's timestamp is stored as a Unix epoch inside a date -r call, e.g.:
echo Restored session: "$(/bin/date -r 1715852825)"Here 1715852825 decodes to Thu May 16 2024 09:47:05 UTC — the moment the terminal was reopened.
Preconditions / Affected
Zsh sessions must be enabled (default on modern macOS). The artifact matters when .zsh_history is missing or tampered but the per-session files remain, or when you want a timestamp that plain .zsh_history (without extended history) does not carry.
What to do
Enumerate the directory and pair histories with their session timestamps:
head 53517CEE-640B-4445-98BD-1A4BEE3FB463.history
cat 53517CEE-640B-4445-98BD-1A4BEE3FB463.sessionRead the epoch from the date -r argument in each .session file to place its .history in time. Identify the live session via $HISTFILE / $TERM_SESSION_ID (the .historynew). Files with no .session pair (the active .historynew) will not have a restore timestamp.
Caveats
The .session timestamp marks only when the terminal was reopened/restored, not when individual commands ran — coarser than fc -lf on a live session. The author frames it as narrowing the window, not pinpointing execution. There is exactly one timestamp per session file, so all commands in a .history can only be bounded to that session's restore time.
Techniques named here
- Zsh session artifact review
- Recovery of deleted .zsh_history
- Live session history extraction
Related in Blue Team & Detection
AI-Coded Malware | Analyzing Vibe-Coded AD Enumeration | Huntress
