On this page 32 sections
- What SSH tunneling is
- Basic pivoting concept
- The three forwarding modes
- Local port forwarding
- Local forwarding example
- How -L works
- Access an internal database
- Access an internal RDP service
- Remote port forwarding
- Remote forwarding example
- How -R works
- Dynamic port forwarding
- How -D works
- Why dynamic forwarding is useful
- Proxychains
- Configure Proxychains
- Nmap through Proxychains
- Why -sT matters through a proxy
- Tunnel without opening a shell
- Run SSH tunnel in the background
- Bind forwarding to localhost
- Multiple forwarding rules
- Use a private key
- Custom SSH port
- Troubleshoot the tunnel
- SSH server forwarding restrictions
- Local vs dynamic forwarding
- Local vs remote forwarding
- Mental model
- Useful SSH tunneling options
- Practical pivoting workflow
- Quick reference
What SSH tunneling is
SSH tunneling transports network connections through an encrypted SSH session. It is useful when an SSH-accessible host can reach another system, network or service that is not directly reachable from your machine.
Basic pivoting concept
An SSH host can act as an intermediate system between your machine and another network. Instead of connecting directly to an internal service, your traffic is sent through the SSH connection and exits from the pivot host.
The three forwarding modes
SSH provides three main forwarding modes. Local forwarding uses -L to expose a remote service locally. Remote forwarding uses -R to expose a client-side or client-reachable service on the remote SSH side. Dynamic forwarding uses -D to create a SOCKS proxy for reaching multiple destinations.
Local port forwarding
Local port forwarding creates a listening port on your machine. Connections sent to that local port travel through the SSH server and are then forwarded to the specified destination.
ssh -L <LOCAL_PORT>:<DESTINATION_HOST>:<DESTINATION_PORT> <USER>@<SSH_SERVER>Local forwarding example
Suppose the SSH pivot host can reach an internal web server at 10.10.10.20:80, but your machine cannot. Port 8080 on your machine can be forwarded through the pivot to that internal web service.
ssh -L 8080:10.10.10.20:80 user@192.168.1.100curl http://127.0.0.1:8080How -L works
The -L format is LOCAL_PORT:DESTINATION_HOST:DESTINATION_PORT. Your local machine listens on LOCAL_PORT. When an application connects there, SSH transports the connection to the SSH server, which then connects to DESTINATION_HOST:DESTINATION_PORT.
ssh -L 3306:10.10.10.30:3306 user@192.168.1.100Access an internal database
Local forwarding is useful for internal databases that are reachable from the pivot host but not directly from your machine.
ssh -L 3306:10.10.10.30:3306 user@192.168.1.100mysql -h 127.0.0.1 -P 3306 -u <USERNAME> -pAccess an internal RDP service
The same technique can forward an internal RDP service to a different local port.
ssh -L 13389:10.10.10.40:3389 user@192.168.1.100Remote port forwarding
Remote port forwarding creates the listening port on the SSH server side. Connections received by that remote port travel back through the SSH tunnel toward a destination reachable from the SSH client.
ssh -R <REMOTE_PORT>:<DESTINATION_HOST>:<DESTINATION_PORT> <USER>@<SSH_SERVER>Remote forwarding example
If a service is available from the SSH client side but not from the SSH server side, -R can expose that service through a port created on the remote SSH host.
ssh -R 8080:127.0.0.1:8000 user@<SSH_SERVER>How -R works
The -R format is REMOTE_PORT:DESTINATION_HOST:DESTINATION_PORT. The listening socket exists on the SSH server side, while the final destination is reached from the SSH client side.
Dynamic port forwarding
Dynamic forwarding creates a SOCKS proxy on your local machine. Unlike -L, it does not define one fixed destination. Applications using the proxy can request connections to different hosts and ports through the SSH server.
ssh -D 1080 user@192.168.1.100How -D works
With -D, SSH listens locally as a SOCKS proxy. Applications connect to the proxy and specify the destination they want to reach. SSH then sends those connections through the pivot host.
ssh -D 1080 <USER>@<PIVOT>Why dynamic forwarding is useful
Dynamic forwarding is usually more flexible than creating many individual -L rules. One SOCKS proxy can be used to reach different hosts and services inside the network that the SSH pivot can access.
Proxychains
Proxychains can force compatible TCP applications to use a configured SOCKS proxy. It is commonly combined with SSH dynamic forwarding when enumerating systems through a pivot host.
ssh -D 1080 user@192.168.1.100proxychains <COMMAND>proxychains ssh user@10.10.10.20Configure Proxychains
The Proxychains configuration needs to point to the SOCKS listener created by SSH. If SSH is listening on local port 1080, configure Proxychains to use 127.0.0.1:1080.
socks5 127.0.0.1 1080Nmap through Proxychains
Some Nmap scan techniques use raw packets and do not operate through a normal SOCKS proxy. TCP connect scans use normal TCP connections and are more appropriate when scanning through Proxychains.
proxychains nmap -sT -Pn -p 22,80,445 10.10.10.20Why -sT matters through a proxy
A SYN scan such as -sS relies on raw packets generated directly by Nmap. Proxychains operates by intercepting normal application TCP connections, so -sT is the safer choice when routing Nmap through a SOCKS proxy.
proxychains nmap -sT -Pn <TARGET>Tunnel without opening a shell
The -N option tells SSH not to execute a remote command. This is useful when the SSH session exists only to maintain forwarding rules.
ssh -N -L 8080:10.10.10.20:80 user@192.168.1.100ssh -N -D 1080 user@192.168.1.100Run SSH tunnel in the background
The -f option asks SSH to move to the background. It is commonly combined with -N when the session is only being used for forwarding.
ssh -f -N -L 8080:10.10.10.20:80 user@192.168.1.100ssh -f -N -D 1080 user@192.168.1.100Bind forwarding to localhost
A forwarding socket can be explicitly bound to 127.0.0.1 so that only applications on your local machine can connect to it. This avoids exposing the forwarded service unnecessarily on other interfaces.
ssh -L 127.0.0.1:8080:10.10.10.20:80 user@192.168.1.100Multiple forwarding rules
A single SSH connection can create several local forwarding rules. This is useful when only a small number of specific internal services need to be accessed.
ssh -L 8080:10.10.10.20:80 -L 13389:10.10.10.30:3389 user@192.168.1.100Use a private key
SSH forwarding works with the same authentication methods as a normal SSH session. If public-key authentication is used, specify the private key with -i.
ssh -i <PRIVATE_KEY> -N -D 1080 <USER>@<PIVOT>Custom SSH port
If the pivot SSH service listens on a non-default port, use -p to specify it.
ssh -p 2222 -N -D 1080 <USER>@<PIVOT>Troubleshoot the tunnel
Verbose SSH output helps identify authentication, forwarding and connection problems. ss can then verify that the expected local forwarding port is actually listening.
ssh -v -L 8080:10.10.10.20:80 user@192.168.1.100ss -lntpSSH server forwarding restrictions
SSH servers can restrict forwarding. AllowTcpForwarding controls whether TCP forwarding is permitted, while GatewayPorts influences whether remotely forwarded ports can listen beyond the loopback interface.
AllowTcpForwarding yes
GatewayPorts noLocal vs dynamic forwarding
Use -L when you know exactly which host and service you need to reach. Use -D when you want a flexible SOCKS proxy capable of reaching multiple hosts and services through the pivot.
ssh -L 8080:10.10.10.20:80 <USER>@<PIVOT>ssh -D 1080 <USER>@<PIVOT>Local vs remote forwarding
With -L, the listening port exists on the SSH client side and traffic is forwarded toward the remote network. With -R, the listening port exists on the SSH server side and traffic travels back through the SSH client toward the requested destination.
Mental model
-L means create a port here so I can reach something over there. -R means create a port over there that sends connections back through me. -D means create a SOCKS proxy here so applications can choose different destinations through the SSH server.
Useful SSH tunneling options
-L creates local forwarding, -R creates remote forwarding, -D creates a dynamic SOCKS proxy, -N prevents execution of a remote command, -f moves SSH to the background, -p specifies the SSH server port, -i specifies a private key and -v enables verbose output.
ssh -L <LOCAL_PORT>:<TARGET>:<PORT> <USER>@<PIVOT>ssh -R <REMOTE_PORT>:<TARGET>:<PORT> <USER>@<SSH_SERVER>ssh -D 1080 <USER>@<PIVOT>Practical pivoting workflow
First identify a reachable SSH host and determine which additional hosts or networks it can access. Use -L for a specific known service, -D for broader access through a SOCKS proxy and -R when the remote SSH side needs access to something reachable from the SSH client. Verify the listening socket and then test connectivity through the tunnel.
ss -lntpQuick reference
These are the SSH forwarding commands worth remembering.
ssh -L <LOCAL_PORT>:<TARGET>:<PORT> <USER>@<PIVOT>ssh -R <REMOTE_PORT>:<TARGET>:<PORT> <USER>@<SSH_SERVER>ssh -D 1080 <USER>@<PIVOT>ssh -N -D 1080 <USER>@<PIVOT>ssh -f -N -D 1080 <USER>@<PIVOT>proxychains ssh <USER>@<INTERNAL_TARGET>proxychains nmap -sT -Pn -p <PORTS> <INTERNAL_TARGET>ss -lntp