iTerm2
iTerm2 Profiles & SSH Configuration
Overview
iTerm2 stores its configuration in the preferences plist ~/Library/Preferences/com.googlecode.iterm2.plist. The forensically richest part is the New Bookmarks array — iTerm2's internal name for the list of user profiles. Each profile is a dictionary describing a saved terminal configuration, and profiles frequently embed the details of remote systems the user connects to: SSH hostnames, ports, usernames, and private-key paths, either as first-class fields or inside a custom launch command such as ssh deploy@prod-web-01.
Profiles map a subject's remote infrastructure. A developer or administrator typically defines a profile per server or environment, so the profile list is effectively an inventory of the machines the user works with — valuable for lateral-movement and scope analysis. Each profile can also carry trigger rules that automatically act on matching terminal output (highlighting text, sending a password, launching a coprocess or script), which reveal automation the user configured and can expose credentials.
Forensic Significance
| Evidence Type | Forensic Value |
|---|---|
| Profile inventory | Remote systems and environments the user routinely accessed |
| SSH host / port / user | Direct mapping of remote infrastructure and credentials-in-use |
| SSH key path | Which private key authenticates to a given host |
| Custom command | The exact command a profile launches (often ssh …) |
| Initial directory | Working directory a session starts in |
| Triggers | Automated actions — password sends, coprocesses, scripts — on terminal output |
| Badge / tags | User labelling that groups hosts by environment or role |
| Dynamic profiles | Externally-managed profiles that may indicate MDM or tooling |
File Locations
| Artifact | Path | Format |
|---|---|---|
| Preferences plist (profiles) | ~/Library/Preferences/com.googlecode.iterm2.plist | Binary or XML plist |
| Dynamic profiles | ~/Library/Application Support/iTerm2/DynamicProfiles/*.json | JSON |
Profiles live in the New Bookmarks array of the plist. Dynamic profiles are a separate mechanism: JSON files under DynamicProfiles/ that iTerm2 loads at launch, each with a top-level Profiles array using the same key vocabulary. Dynamic profiles are commonly written by scripts, tooling, or device management, so their presence is itself notable.
cfprefsd caching
On a live system, the on-disk com.googlecode.iterm2.plist may lag behind iTerm2's in-memory state because macOS caches preferences in cfprefsd. For maximum fidelity, prefer analysis of a disk image or flush preferences before collection. This is a macOS-wide plist behaviour, not specific to iTerm2.
Profile Fields
Each profile dictionary in the New Bookmarks array may contain the following keys (plist key on the left, meaning on the right). Dynamic profile JSON files use the same key names.
| Plist Key | Description |
|---|---|
Name | Profile display name |
Guid | Unique profile identifier |
Command | Login command (used when the profile runs a command instead of a login shell) |
Custom Command | Custom launch command string — frequently ssh user@host |
Host | SSH hostname (iTerm2 SSH integration profiles) |
Port | SSH port |
Username | SSH / login username |
SSH Key | Path to the private key used for the connection |
Working Directory | Initial working directory for the session |
Badge Text | On-screen badge overlay (often the hostname or environment) |
Tags | User-assigned labels grouping the profile |
Triggers | Array of trigger rules (see below) |
SSH Detection
A profile is treated as an SSH connection when it carries a non-empty Host field, or when its Command/Custom Command invokes the ssh client (a whole ssh token, or a path ending in /ssh). macfor sets an is_ssh flag on the emitted record accordingly, so SSH targets can be filtered out of the full profile list quickly.
Triggers
Triggers fire an action when a regular expression matches terminal output. Each trigger dictionary in a profile's Triggers array contains:
| Key | Description |
|---|---|
regex | Regular expression matched against terminal output |
action | Trigger action class (see table) |
parameter | Action-specific parameter (text to send, command to run, highlight colour, …) |
partial | Whether the trigger may fire on a partial (unterminated) line |
enabled | Whether the trigger is active |
Common action values and their forensic meaning:
| Action | Meaning | Forensic Note |
|---|---|---|
PasswordTrigger | Send a stored password when prompted | Indicates automated credential entry to a keychain-stored secret |
SendTextTrigger | Send fixed text to the terminal | parameter may contain literal credentials or commands |
CoprocessTrigger | Launch a coprocess | External process wired to terminal output |
ScriptTrigger / RunCommandTrigger | Run a command/script | Automation that may execute arbitrary code |
HighlightTrigger | Colour matching text | Benign, but reveals what the user watches for |
BounceTrigger | Bounce the Dock icon | Benign notification behaviour |
Triggers can carry credentials
SendTextTrigger parameters sometimes contain plaintext passwords or tokens that a user configured to auto-send. PasswordTrigger references a secret stored in the login keychain. Review every trigger parameter value — it is a common location for accidentally-exposed credentials.
Parsed Record Schema
macfor emits one iterm2_profile record per profile (from both the plist and any dynamic profile files).
| Field | Type | Description |
|---|---|---|
type | string | Always iterm2_profile |
app | string | Always iterm2 |
user | string | Local user account the profile belongs to |
name | string | Profile name |
guid | string | Profile GUID |
custom_command | string | Custom launch command, if set |
command | string | Login command, if set |
host | string | SSH hostname, if set |
port | int | SSH port, if set |
username | string | SSH / login username, if set |
ssh_key_path | string | Private-key path, if set |
initial_directory | string | Initial working directory, if set |
badge_text | string | Badge overlay text, if set |
tags | []string | User-assigned tags |
triggers | []object | Trigger rules (regex, action, parameter, partial, enabled) |
is_ssh | bool | Whether the profile represents an SSH connection |
is_dynamic | bool | Whether the profile came from a DynamicProfiles/ JSON file |
source_file | string | Absolute path to the plist or dynamic-profile file |
Manual Inspection
# Convert the (binary) plist to readable XML/JSON
plutil -convert xml1 -o - ~/Library/Preferences/com.googlecode.iterm2.plist | less
# Extract profile names and any SSH hosts
plutil -convert json -o - ~/Library/Preferences/com.googlecode.iterm2.plist | \
python3 -c "
import sys, json
prefs = json.load(sys.stdin)
for p in prefs.get('New Bookmarks', []):
print(p.get('Name'), '|', p.get('Custom Command',''), '|', p.get('Host',''), p.get('Username',''))
"
# List dynamic profiles
ls -la ~/Library/Application\ Support/iTerm2/DynamicProfiles/
Analysis Notes
- Infrastructure map: The profile list is a curated inventory of the remote systems a subject worked with. Combine
host,username,port, andcustom_commandto enumerate SSH targets. - Key-to-host mapping:
ssh_key_pathlinks a specific private key to a host, helping attribute authentication and identify which key material to preserve. - Custom commands: Profiles often launch SSH via
Custom Commandrather than the structuredHostfield — always inspectcustom_command, not justhost. - Dynamic profiles: Externally-managed. If a
DynamicProfiles/file was written by tooling or an MDM, it may reveal an automated deployment or a persistence mechanism; note its modification time and origin. - Trigger review: Enumerate every trigger's
actionandparameter. Password/send-text triggers are a credential-exposure hotspot; coprocess/script triggers are an automation and potential code-execution vector. - Cross-reference SSH artifacts: Correlate hosts and usernames with
~/.ssh/config,~/.ssh/known_hosts, and the iTerm2 remote-host history.
Tool Support
| Tool | Support |
|---|---|
| macfor | Full extraction of profiles, SSH fields, triggers, and dynamic profiles with an is_ssh flag |
| plutil / defaults (macOS built-in) | Read and convert the preferences plist |
| python3 | Parse converted JSON to enumerate profiles and triggers |