On this page 32 sections
  1. What SSH tunneling is
  2. Basic pivoting concept
  3. The three forwarding modes
  4. Local port forwarding
  5. Local forwarding example
  6. How -L works
  7. Access an internal database
  8. Access an internal RDP service
  9. Remote port forwarding
  10. Remote forwarding example
  11. How -R works
  12. Dynamic port forwarding
  13. How -D works
  14. Why dynamic forwarding is useful
  15. Proxychains
  16. Configure Proxychains
  17. Nmap through Proxychains
  18. Why -sT matters through a proxy
  19. Tunnel without opening a shell
  20. Run SSH tunnel in the background
  21. Bind forwarding to localhost
  22. Multiple forwarding rules
  23. Use a private key
  24. Custom SSH port
  25. Troubleshoot the tunnel
  26. SSH server forwarding restrictions
  27. Local vs dynamic forwarding
  28. Local vs remote forwarding
  29. Mental model
  30. Useful SSH tunneling options
  31. Practical pivoting workflow
  32. Quick reference
01

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.

02

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.

03

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.

04

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.

General syntax
ssh -L <LOCAL_PORT>:<DESTINATION_HOST>:<DESTINATION_PORT> <USER>@<SSH_SERVER>
05

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.

Create local forwarding
ssh -L 8080:10.10.10.20:80 user@192.168.1.100
Access the forwarded service
curl http://127.0.0.1:8080
06

How -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.

Forward internal MySQL
ssh -L 3306:10.10.10.30:3306 user@192.168.1.100
07

Access an internal database

Local forwarding is useful for internal databases that are reachable from the pivot host but not directly from your machine.

Forward MySQL
ssh -L 3306:10.10.10.30:3306 user@192.168.1.100
Connect through the tunnel
mysql -h 127.0.0.1 -P 3306 -u <USERNAME> -p
08

Access an internal RDP service

The same technique can forward an internal RDP service to a different local port.

Forward internal RDP
ssh -L 13389:10.10.10.40:3389 user@192.168.1.100
09

Remote 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.

General syntax
ssh -R <REMOTE_PORT>:<DESTINATION_HOST>:<DESTINATION_PORT> <USER>@<SSH_SERVER>
10

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.

Create remote forwarding
ssh -R 8080:127.0.0.1:8000 user@<SSH_SERVER>
11

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.

12

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.

Create SOCKS proxy
ssh -D 1080 user@192.168.1.100
13

How -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.

SOCKS proxy on port 1080
ssh -D 1080 <USER>@<PIVOT>
14

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.

15

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.

Create SSH SOCKS proxy
ssh -D 1080 user@192.168.1.100
Run command through Proxychains
proxychains <COMMAND>
SSH to an internal host
proxychains ssh user@10.10.10.20
16

Configure 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 entry
socks5 127.0.0.1 1080
17

Nmap 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.

TCP connect scan through proxy
proxychains nmap -sT -Pn -p 22,80,445 10.10.10.20
18

Why -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.

Recommended scan type
proxychains nmap -sT -Pn <TARGET>
19

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.

Local tunnel only
ssh -N -L 8080:10.10.10.20:80 user@192.168.1.100
SOCKS tunnel only
ssh -N -D 1080 user@192.168.1.100
20

Run 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.

Background local tunnel
ssh -f -N -L 8080:10.10.10.20:80 user@192.168.1.100
Background SOCKS tunnel
ssh -f -N -D 1080 user@192.168.1.100
21

Bind 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.

Local-only bind
ssh -L 127.0.0.1:8080:10.10.10.20:80 user@192.168.1.100
22

Multiple 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.

Forward HTTP and RDP
ssh -L 8080:10.10.10.20:80 -L 13389:10.10.10.30:3389 user@192.168.1.100
23

Use 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.

Tunnel using private key
ssh -i <PRIVATE_KEY> -N -D 1080 <USER>@<PIVOT>
24

Custom SSH port

If the pivot SSH service listens on a non-default port, use -p to specify it.

SOCKS tunnel through custom SSH port
ssh -p 2222 -N -D 1080 <USER>@<PIVOT>
25

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.

Verbose tunnel
ssh -v -L 8080:10.10.10.20:80 user@192.168.1.100
Check listening ports
ss -lntp
26

SSH 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.

Relevant sshd_config options
AllowTcpForwarding yes
GatewayPorts no
27

Local 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.

Known single service
ssh -L 8080:10.10.10.20:80 <USER>@<PIVOT>
Multiple destinations
ssh -D 1080 <USER>@<PIVOT>
28

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.

29

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.

30

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.

Local
ssh -L <LOCAL_PORT>:<TARGET>:<PORT> <USER>@<PIVOT>
Remote
ssh -R <REMOTE_PORT>:<TARGET>:<PORT> <USER>@<SSH_SERVER>
Dynamic
ssh -D 1080 <USER>@<PIVOT>
31

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.

Check local tunnel ports
ss -lntp
32

Quick reference

These are the SSH forwarding commands worth remembering.

Local forwarding
ssh -L <LOCAL_PORT>:<TARGET>:<PORT> <USER>@<PIVOT>
Remote forwarding
ssh -R <REMOTE_PORT>:<TARGET>:<PORT> <USER>@<SSH_SERVER>
Dynamic SOCKS proxy
ssh -D 1080 <USER>@<PIVOT>
Tunnel without shell
ssh -N -D 1080 <USER>@<PIVOT>
Background tunnel
ssh -f -N -D 1080 <USER>@<PIVOT>
Proxychains SSH
proxychains ssh <USER>@<INTERNAL_TARGET>
Proxychains Nmap
proxychains nmap -sT -Pn -p <PORTS> <INTERNAL_TARGET>
Check tunnel listeners
ss -lntp