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
Tools
Compare all tools- Hash ExtractorIn browser
- logCLI · built into macOS
- macos-UnifiedLogsLibrary · open source
- ssh-keygenCLI · built into macOS
- statCLI · built into macOS
- grepCLI · built into macOS
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
| Source | Path | Notes |
|---|---|---|
| Authorized keys | ~/.ssh/authorized_keys | Keys allowed to log in as that user |
| Known hosts | ~/.ssh/known_hosts | Hosts this user connected to |
| Client config | ~/.ssh/config, /private/etc/ssh/ssh_config | Aliases, jump hosts, forwarded ports |
| User keys | ~/.ssh/id_* and any other private keys | Encrypted 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.plist | com.openssh.sshd key: false means enabled |
| Access group | com.apple.access_ssh in dslocal | Limits Remote Login to listed users when present |
| sshd events | Unified 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 forrootor an admin, or a new file insshd_config.dthat enables root login or password authentication. - Outbound activity: every host in
known_hostswas 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
| Item | What 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 line | Host name or ` |
authorized_keys options | command=, 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_hostsonly 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 ansshdUnified Log predicate. - UAC collects
authorized_keys,known_hostsand 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 onorlaunchctlcommand in shell history and sudo logs. - Check
root's.sshin/private/var/root/.sshas well as every user folder. - Outbound
sshcommands in shell history plusknown_hostsentries give you the next hosts to examine. - Port forwards (
-L,-R,-D) in shell history or~/.ssh/configpoint to tunnelling. - Newer OpenSSH releases split per-connection work into
sshd-session; always query both process names so an upgrade does not hide events.