Skip to content

NetworkPersistenceLogs

SSH and Remote Login on macOS: Keys, Hosts and Logs

macOS Remote Login (sshd) leaves authorized_keys, known_hosts, sshd configuration and Unified Log events that show inbound and outbound SSH activity.

Location
~/.ssh/ and /private/etc/ssh/
Proves
Inbound SSH logons (who, from which IP, with which key or password), outbound SSH targets, and key-based persistence
Timestamps
Unified Log entries in UTC; file system times of authorized_keys, known_hosts and host keys
Access
Owning user for ~/.ssh; root for /private/etc/ssh and the log store
Retention
Key files until edited; sshd events follow Unified Log rotation (days to weeks)
Collection
Aftermath, UAC, log collect, ditto

What it is

macOS ships OpenSSH. The server side is Remote Login in System Settings (Sharing), which enables the com.openssh.sshd launchd job. The client side is the ssh, scp and sftp commands every user has. Both leave durable files plus log events, and both matter: inbound SSH is a classic lateral movement and access path, while outbound SSH from a compromised Mac reveals where an intruder went next.

Where it lives

SourcePathNotes
Authorized keys~/.ssh/authorized_keysKeys allowed to log in as that user
Known hosts~/.ssh/known_hostsHosts this user connected to
Client config~/.ssh/config, /private/etc/ssh/ssh_configAliases, jump hosts, forwarded ports
User keys~/.ssh/id_* and any other private keysEncrypted or not
Server config/private/etc/ssh/sshd_config, /private/etc/ssh/sshd_config.d/Apple drop-in files ship on recent releases
Host keys/private/etc/ssh/ssh_host_*_key(.pub)Generated when sshd first needs them
Service state/private/var/db/com.apple.xpc.launchd/disabled.plistcom.openssh.sshd key: false means enabled
Access groupcom.apple.access_ssh in dslocalLimits Remote Login to listed users when present
sshd eventsUnified Logs, process sshd (and sshd-session on newer OpenSSH)See below

What it proves

  • An inbound logon: account, source IP and port, authentication method (publickey, password, keyboard-interactive) and, for keys, the key fingerprint.
  • Failed attempts and invalid user names, which show brute force or credential testing.
  • Persistence: a key added to authorized_keys, especially for root or an admin, or a new file in sshd_config.d that enables root login or password authentication.
  • Outbound activity: every host in known_hosts was contacted at least once by that user, unless the file was copied from elsewhere.

Key fields

Typical sshd messages (OpenSSH wording):

Accepted publickey for <user> from 203.0.113.7 port 51422 ssh2: ED25519 SHA256:<fingerprint>
Accepted keyboard-interactive/pam for <user> from 203.0.113.7 port 51430 ssh2
Failed password for invalid user admin from 198.51.100.4 port 40022 ssh2
ItemWhat to extract
Accepted <method> for <user>Successful logon and method
from <ip> port <n>Source address
SHA256:<fingerprint>Match with ssh-keygen -lf authorized_keys output
known_hosts lineHost name or `
authorized_keys optionscommand=, from=, no-pty restrictions or their absence

Apple's OpenSSH does not hash known_hosts by default in most configurations, so host names are usually readable. Hashed entries (|1|salt|hash) can be tested against candidate names with ssh-keygen -F <host>.

Timestamps

Unified Log entries carry UTC instants; print with --timezone UTC. For the key files, the modification time of authorized_keys shows the last change, and birth time shows its creation. The birth time of the host keys approximates the first time sshd ran on the Mac (not a reliable indicator after an OS reinstall or migration).

stat -f '%SB | %Sm | %N' -t '%Y-%m-%dT%H:%M:%S%z' ~/.ssh/* /private/etc/ssh/ssh_host_*

Retention

  • Key and config files persist until edited, and survive OS upgrades and Migration Assistant transfers.
  • sshd events follow Unified Log rotation, so older logons may be gone within days on a busy system. Collect a log archive early.
  • known_hosts only grows unless edited; it can reach back years.

Collection

sudo ditto /private/etc/ssh ./case/etc_ssh
sudo ditto /Users/<user>/.ssh ./case/ssh_<user>
sudo ditto /private/var/root/.ssh ./case/ssh_root
sudo log collect --output ./case/host.logarchive
  • Aftermath copies /etc/ssh/ and each user's .ssh/, and runs an sshd Unified Log predicate.
  • UAC collects authorized_keys, known_hosts and public keys.

Parsing

# Successful and failed SSH logons
log show --archive host.logarchive --timezone UTC --style syslog \
  --predicate '(process == "sshd" OR process == "sshd-session") AND
               (eventMessage CONTAINS "Accepted" OR eventMessage CONTAINS "Failed"
                OR eventMessage CONTAINS "Invalid user")'

# Fingerprints of authorised keys, to match against the log
ssh-keygen -lf ./case/ssh_<user>/authorized_keys

# Is Remote Login enabled?
plutil -p /private/var/db/com.apple.xpc.launchd/disabled.plist | grep sshd

For encrypted private keys found on the system, the Hash Extractor can turn them into a crackable hash when the investigation is authorised to do so.

Investigator tips

  • Remote Login enabled on a laptop that has no reason to accept SSH is a finding in itself; look for the systemsetup -setremotelogin on or launchctl command in shell history and sudo logs.
  • Check root's .ssh in /private/var/root/.ssh as well as every user folder.
  • Outbound ssh commands in shell history plus known_hosts entries give you the next hosts to examine.
  • Port forwards (-L, -R, -D) in shell history or ~/.ssh/config point to tunnelling.
  • Newer OpenSSH releases split per-connection work into sshd-session; always query both process names so an upgrade does not hide events.

See also