SSH Port Forwarding (Advanced)
#Overview
SSH port forwarding tunnels individual ports through an SSH connection. This section covers advanced SSH forwarding beyond the basic -L and -R flags, including ProxyCommand, ProxyJump, and persistent tunneling with autossh.
#Local Forwarding (-L) Advanced
#Multiple Forwards in One Session
# Forward multiple ports through a single SSH connection
ssh -L 8080:172.16.0.10:80 -L 3306:172.16.0.20:3306 -L 6379:172.16.0.30:6379 user@pivot
# Bind to all interfaces (not just localhost)
ssh -L 0.0.0.0:8080:172.16.0.10:80 user@pivot
# Bind to specific interface
ssh -L 192.168.1.50:8080:172.16.0.10:80 user@pivot
BASH
#GatewayPorts
# By default, remote forwards only bind to localhost on the SSH server
# Enable gateway ports to allow remote hosts to connect to your forwarded port
ssh -R 0.0.0.0:8080:127.0.0.1:80 user@pivot
# On the SSH server, edit /etc/ssh/sshd_config:
GatewayPorts yes # or clientspecified
# Restart SSH
sudo systemctl restart sshd
BASH
#Remote Forwarding (-R) Advanced
#Reverse SOCKS Proxy via SSH
# Create a reverse SOCKS proxy on the pivot host
ssh -R 1080 user@pivot
# On the pivot, any user can use the SOCKS proxy:
# curl -x socks5://127.0.0.1:1080 http://internal-service/
BASH
#Reverse Port Forward for Callbacks
# Forward pivot's port 8080 back to attacker's local port 80
ssh -R 8080:127.0.0.1:80 user@pivot
# Now on the pivot: http://127.0.0.1:8080 reaches attacker's port 80
# Useful for serving payloads or receiving reverse shells through the pivot
BASH
#SSH ProxyCommand
ProxyCommand lets you tunnel SSH connections through an intermediate host without requiring a direct route. It's the building block for multi-hop SSH chains.
# Connect to target through pivot, without ProxyJump
ssh -o ProxyCommand="ssh -W %h:%p user@pivot" user@target
# How it works:
# 1. SSH connects to pivot
# 2. Runs "ssh -W target:22" on pivot (netcat mode)
# 3. SSH session to target flows through that connection
# With netcat instead of SSH (when pivot doesn't have SSH client)
ssh -o ProxyCommand="ssh user@pivot nc %h %p" user@target
# With socat (more reliable than nc)
ssh -o ProxyCommand="ssh user@pivot socat - %h:%p" user@target
BASH
#ProxyCommand with SSH Config
# ~/.ssh/config
Host target
HostName 172.16.0.50
User admin
ProxyCommand ssh -W %h:%p pivot
Host pivot
HostName 10.10.10.10
User ubuntu
IdentityFile ~/.ssh/pivot_key
# Now simply:
ssh target
# Automatically tunnels through pivot
BASH
#SSH ProxyJump (-J)
ProxyJump is the modern, simplified replacement for ProxyCommand. Available since OpenSSH 7.3.
# Single jump
ssh -J user@pivot user@target
# Multiple jumps (chain through 2+ pivots)
ssh -J user@pivot1,user@pivot2 user@target
# With SSH config
Host target
HostName 172.16.0.50
ProxyJump pivot1,pivot2
# Now:
ssh target
BASH
#ProxyJump with Dynamic Forwarding
# Jump through pivot AND create SOCKS proxy
ssh -J user@pivot -D 1080 user@target
# Jump through pivot, forward a port from beyond target
ssh -J user@pivot -L 8080:192.168.1.10:80 user@target
BASH
#autossh (Persistent Tunnels)
autossh automatically restarts SSH sessions and port forwarding when they drop. Essential for long-running tunnels during multi-day engagements.
# Install
apt install autossh
# Basic persistent local forward
autossh -M 0 -f -N -L 8080:172.16.0.10:80 user@pivot
# -M 0: Disable monitoring port (use ServerAliveInterval instead)
# -f: Run in background
# -N: No remote command
# With keepalive (recommended)
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-L 8080:172.16.0.10:80 \
user@pivot
# Persistent reverse forward
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-R 8080:127.0.0.1:80 \
user@pivot
# Persistent SOCKS proxy
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-D 1080 \
user@pivot
# As systemd service (survives reboot)
cat > /etc/systemd/system/ssh-tunnel.service << EOF
[Unit]
Description=SSH Tunnel
After=network.target
[Service]
User=operator
ExecStart=/usr/bin/autossh -M 0 -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 8080:172.16.0.10:80 user@pivot
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable ssh-tunnel
sudo systemctl start ssh-tunnel
BASH
#Windows SSH Forwarding
#OpenSSH (Windows 10+)
# OpenSSH is built into Windows 10/11 and Server 2019+
# Local forward
ssh -L 8080:172.16.0.10:80 user@pivot
# Remote forward
ssh -R 8080:127.0.0.1:80 user@pivot
# Dynamic forward (SOCKS)
ssh -D 1080 user@pivot
POWERSHELL
#Plink (PuTTY)
# Local forward
plink.exe -L 8080:172.16.0.10:80 user@pivot
# Remote forward
plink.exe -R 8080:127.0.0.1:80 user@pivot
# With password (non-interactive)
plink.exe -pw password -R 8080:127.0.0.1:80 user@pivot
# Dynamic forward (SOCKS)
plink.exe -D 1080 user@pivot
CMD
#Common Pitfalls
- GatewayPorts disabled: By default,
-Rforwards only bind to127.0.0.1on the server. Other hosts can't reach the forwarded port. EnableGatewayPorts yesorclientspecifiedinsshd_config. - Connection drops: SSH tunnels die when the SSH session drops. Use
autosshfor persistence. - Port conflicts: If the local or remote port is already in use, SSH fails silently. Check with
netstat -tlnporss -tlnp. - Firewall blocks SSH: If port 22 outbound is blocked, try SSH over port 443 or use Chisel/Ligolo instead.
- ProxyJump version: ProxyJump requires OpenSSH 7.3+. Use ProxyCommand on older systems.
#OPSEC Considerations
- SSH tunnels show up in
netstatandssoutput as established connections GatewayPorts yesin sshd_config is a detectable configuration change- autossh processes are visible in process listings
- SSH config files in
~/.ssh/configare artifacts that persist - Plink.exe is a well-known red team tool that may trigger AV
#Cross-References
- SOCKS & HTTP Proxies — SSH -D dynamic forwarding
- Chaining Proxies — Multi-hop SSH chains with ProxyJump
- Native Port Forwarding — Non-SSH port forwarding methods
- 07 - Post-Exploitation — Basic SSH -L/-R