How to Pivot Through Networks Using SSH Tunneling

how to pivot through networks using ssh tunneling

Network pivoting is one of the most critical techniques in internal penetration testing. Once you’ve compromised an internet-facing host, you rarely stop there – the real targets (domain controllers, database servers, internal APIs) are buried deeper in network segments with no direct internet access. Pivoting lets you route your attack traffic through a compromised host to reach those otherwise-inaccessible systems.

SSH tunneling is the go-to mechanism for this in authorized penetration tests. It’s built into every Linux and macOS system, requires no additional tooling on the pivot host (beyond an SSH service or client), and provides encrypted traffic that blends in better than raw netcat pipes or custom implants. When you have valid SSH credentials or an SSH key on a host, you have a fully functional pivot point.

This guide covers the three core SSH pivoting techniques:

  • Local port forwarding – reach a service on a remote network through a local port
  • Remote port forwarding -” expose your local attacker tooling to an internal host
  • Dynamic SOCKS proxy – route arbitrary TCP traffic through the pivot using proxychains

All examples assume an authorized pentest engagement.


Tools Required

ToolPurposeInstall
SSH clientCore tunneling enginePre-installed on Linux/macOS; OpenSSH on Windows
ProxyChains (or ProxyChains-NG)Route arbitrary tools through a SOCKS proxyapt install proxychains4
NmapNetwork and port scanning through the pivotapt install nmap
Metasploit FrameworkExploitation and post-exploitation through the proxyapt install metasploit-framework
curl / netcatConnectivity testing through tunnelsPre-installed on most Linux systems

Network assumptions for this guide:

Attacker:         10.10.14.5  (your Kali box)
Pivot host:       10.10.11.20 (compromised, internet-facing, SSH on port 22)
Internal target:  192.168.10.50 (only reachable from pivot host)
Internal subnet:  192.168.10.0/24

Step-by-Step Instructions

1. Local Port Forwarding

Local port forwarding binds a port on your attacker machine and forwards all connections to a host:port reachable from the pivot host. This is the simplest case: you want to interact with a single internal service as if it were running locally.

Syntax:

ssh -L [local_port]:[target_host]:[target_port] [user]@[pivot_host]

Example – forward internal RDP to local port 3389:

ssh -L 3389:192.168.10.50:3389 pentest@10.10.11.20

Now connect your RDP client to 127.0.0.1:3389 and your traffic transparently reaches 192.168.10.50:3389 through the tunnel.

Example – forward an internal web application to local port 8080:

ssh -L 8080:192.168.10.50:80 pentest@10.10.11.20

Open http://127.0.0.1:8080 in your browser to interact with the internal app.

Non-interactive / background tunnels:

Add -N (no remote command) and -f (go to background) for persistent tunnels that don’t spawn a shell:

ssh -N -f -L 8080:192.168.10.50:80 pentest@10.10.11.20

2. Remote Port Forwarding

Remote port forwarding works in the opposite direction: it binds a port on the pivot host (or another remote host) and forwards connections back to your attacker machine. Use this when you need an internal host to call back to your listener – for example, to catch a reverse shell from a host that can only reach the pivot.

Syntax:

ssh -R [remote_port]:[local_host]:[local_port] [user]@[pivot_host]

Example – expose your Metasploit listener to the internal network:

ssh -R 4444:127.0.0.1:4444 pentest@10.10.11.20

This binds port 4444 on the pivot host. When the internal target executes a reverse payload connecting to 10.10.11.20:4444, that connection is forwarded back to your Metasploit handler at 127.0.0.1:4444.

Note: By default, GatewayPorts is disabled on most SSH servers, so the remote-forwarded port only listens on 127.0.0.1 on the pivot. If the internal target needs to reach it from a different host, you need either GatewayPorts yes in sshd_config (rarely available) or a second hop.


3. Dynamic SOCKS Proxy with -D

A dynamic SOCKS proxy is the most flexible technique. It turns the SSH connection into a SOCKS5 proxy server on your local machine, letting you route any TCP connection through the pivot to any host the pivot can reach. One tunnel, entire subnet.

Syntax:

ssh -D [local_socks_port] [user]@[pivot_host]

Example – open SOCKS proxy on local port 1080:

ssh -N -f -D 1080 pentest@10.10.11.20

You now have a SOCKS5 proxy at 127.0.0.1:1080. Anything you route through it exits from the pivot host into the 192.168.10.0/24 network.


4. Using ProxyChains with the SOCKS Proxy

ProxyChains wraps standard Linux tools to transparently route them through SOCKS proxies. Most tools (nmap, curl, netcat, impacket scripts) work with it.

Configure ProxyChains (/etc/proxychains4.conf):

# Comment out or remove any existing proxy lines at the bottom
# Then add:
[ProxyList]
socks5  127.0.0.1 1080

Also set the chain type to dynamic_chain or strict_chain (dynamic is more forgiving):

dynamic_chain

Scan the internal subnet through the pivot:

proxychains4 nmap -sT -Pn -p 22,80,443,445,3389,8080 192.168.10.0/24

Use -sT (TCP connect scan), not -sS (SYN scan). SYN scans require raw sockets and do not proxy correctly through SOCKS.

Curl an internal web service:

proxychains4 curl -s http://192.168.10.50/admin/

Run an Impacket SMB module through the proxy:

proxychains4 python3 /usr/share/doc/python3-impacket/examples/smbclient.py pentest@192.168.10.50

5. Chaining Multiple Hops

When the target is behind two or more network segments, chain the tunnels. Set up the first SOCKS proxy to the first pivot, then within that session establish a second SOCKS proxy to the second pivot.

Hop 1: SOCKS proxy to first pivot (10.10.11.20):

ssh -N -f -D 1080 pentest@10.10.11.20

Hop 2: Through proxychains, SSH to second pivot (192.168.10.50) and open another SOCKS proxy on port 1081:

proxychains4 ssh -N -f -D 1081 pentest@192.168.10.50

Update /etc/proxychains4.conf to chain both:

[ProxyList]
socks5  127.0.0.1 1080
socks5  127.0.0.1 1081

Traffic now traverses both pivots to reach the third network segment.


Example Outputs

Establishing the SOCKS proxy

attacker@kali:~$ ssh -N -f -D 1080 pentest@10.10.11.20
pentest@10.10.11.20's password: [password]
attacker@kali:~$

No output on success – the process moves to the background.

Verifying the tunnel is alive

attacker@kali:~$ ss -tlnp | grep 1080
LISTEN  0  128  127.0.0.1:1080  0.0.0.0:*  users:(("ssh",pid=14832,fd=5))

Proxychains nmap scan through the pivot

attacker@kali:~$ proxychains4 nmap -sT -Pn -p 22,80,443,445,3389 192.168.10.50
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16

Starting Nmap 7.94 ( https://nmap.org )
Host is up (0.085s latency).

PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
443/tcp  filtered https
445/tcp  open  microsoft-ds
3389/tcp open  ms-wbt-server

Nmap done: 1 IP address (1 host up) scanned in 12.43 seconds

Local port forward – connecting to internal RDP

attacker@kali:~$ ssh -N -f -L 3389:192.168.10.50:3389 pentest@10.10.11.20
attacker@kali:~$ xfreerdp /v:127.0.0.1:3389 /u:administrator /p:Password123
[19:12:04:231] [14911:14912] [INFO][com.freerdp.core] - Connecting to 127.0.0.1:3389...
[19:12:04:887] [14911:14912] [INFO][com.freerdp.core.transport] - BIO_write returned a failure: -1
[19:12:05:113] [14911:14912] [INFO][com.freerdp.core] - Connection established.

Common Mistakes

1. Forgetting to route tools through ProxyChains

Running nmap 192.168.10.50 without proxychains4 will attempt to connect directly from your attacker box, not through the pivot. If the internal host isn’t routable from your machine, the scan silently fails or returns all filtered. Always prefix tools with proxychains4 when targeting internal hosts.

2. Using SYN scan (-sS) instead of TCP connect scan (-sT) with ProxyChains

SYN scans craft raw packets at the IP layer and cannot be proxied through SOCKS. ProxyChains only operates at the TCP layer. Always use -sT with Nmap when proxying, and accept that the scan will be slower.

3. Tunnel drops breaking long-running operations

SSH connections time out due to idle keepalive settings. For operations that run for extended periods (full subnet scans, slow exploitation), keep the tunnel alive:

ssh -N -f -D 1080 -o ServerAliveInterval=60 -o ServerAliveCountMax=5 pentest@10.10.11.20

4. Binding the SOCKS port on the wrong interface

-D 1080 binds on 127.0.0.1 by default. If you’re running your tools on a different machine and need them to reach this proxy, use -D 0.0.0.0:1080 – but be aware this exposes the proxy to your entire network.

5. MTU fragmentation issues through deeply nested tunnels

Multi-hop tunnels layer encryption overhead and reduce the effective MTU. Large payloads (file transfers, full TCP windows) can fragment unexpectedly. If transfers stall or connections drop mid-session, add MTU clamping or break large transfers into smaller chunks.

6. Port conflicts on the attacker machine

If you use -L 80:... and something already listens on port 80, the tunnel silently fails to bind. Use high-numbered local ports (e.g., 8080, 8443, 13389) to avoid conflicts.

7. ProxyChains not installed or misconfigured

ProxyChains-NG (proxychains4) and the older ProxyChains (proxychains) have slightly different config files. Confirm which binary and config file you’re using. Running proxychains4 curl http://192.168.10.50 and getting a direct connection failure (not a proxy error) usually means the config file isn’t being picked up.


Summary

SSH tunneling gives penetration testers a reliable, encrypted method to reach internal network segments during authorized engagements. The three core techniques cover the majority of real-world scenarios:

TechniqueFlagUse Case
Local port forward-LAccess a specific internal service locally
Remote port forward-RReceive callbacks from internal hosts
Dynamic SOCKS proxy-DRoute all tools through the pivot

Combine -D with ProxyChains to turn a single SSH session into a full pivot point for tools like Nmap, Metasploit, Impacket, and curl. For deeper networks, chain multiple SOCKS hops. Keep tunnels alive with ServerAliveInterval, use TCP connect scans through proxies, and always confirm your tools are actually routing through the proxy before launching large operations.


This article is intended for authorized penetration testing and educational purposes only. Always obtain explicit written permission before conducting network testing.