SSH Tunelling
Last updated
Was this helpful?
Last updated
Was this helpful?
SSH tunneling encapsulates arbitrary network traffic inside an encrypted SSH connection. It’s useful for securely accessing services across restrictive networks or pivoting through compromised systems. Port forwarding with SSH is controlled using options like -L
, -R
, and -D
, depending on the direction of traffic.
Local port forwarding is commonly used when you have SSH access to a pivot machine and want to access a service only it can reach. Unlike tools like socat
or chisel
, SSH is encrypted by design and blends into normal admin traffic. Expanding the :
We compromised CONFLUENCE01
(192.168.125.63
) and have a reverse shell.
From there, we can SSH to PGDATABASE01
(10.4.125.215
).
PGDATABASE01
has access to an SMB server on HRSHARES
(172.16.185.217:445
).
Our goal is to forward TCP port 445
on 172.16.185.217
to TCP port 4455
on CONFLUENCE01
, then access it from our Kali machine.
Why Local Port Forwarding?
Service (SMB) isn’t directly reachable from Kali.
Service is reachable by PGDATABASE01
.
Tunnel traffic through an SSH session on CONFLUENCE01
→ PGDATABASE01
→ HRSHARES
.
Local port forwarding can only connect to one specific destination per SSH connection. With local dynamic port forwarding, instead of connecting to just one destination, it creates a SOCKS proxy server on the SSH client.
A SOCKS proxy works like a mailroom — it takes in network requests, reads where they need to go, and forwards them through the SSH connection to any destination the SSH server can reach. The only requirement is that the software sending the requests must be able to use SOCKS. If it isn’t, extra steps are needed to make it compatible.
In our scenario, instead of connecting directly to port 445
on HRSHARES
, we’ll do it through our SSH SOCKS proxy. The problem is that smbclient
doesn’t have built-in support for SOCKS proxies. Since SOCKS proxies expect traffic in a specific format, smbclient
’s normal traffic won’t work through it. To solve this, we’ll use proxychains
— a tool that forces other programs to send their traffic through a proxy. proxychains
works well with most dynamically linked programs but not with statically linked ones. To use proxychains
:
Although we use SOCKS5 here (which supports extra features like authentication and IPv6), SOCKS4 could also work. Always check which version the proxy server supports during engagements.
Why Dynamic Port Forwarding?
Dynamically access any host and port reachable by the SSH server without needing to forward each port individually.
Particularly useful in complex pivot scenarios where multiple services across subnets are in play.
For nmap
via proxychains
the -sT
flag must be used; the default sudo
scan (-sS
) won't work.
-sS
(SYN)
Raw packets (no connect())
❌ Not compatible
-sT
(Connect)
Standard TCP connect() calls
✅ Fully compatible
In earlier examples, we could freely connect to ports on CONFLUENCE01
’s network. In real environments, though, firewalls usually block inbound traffic to protect systems. Outbound traffic is typically less restricted, which means it's easier to SSH out of a network than into it. This is where SSH remote port forwarding comes in. It works like a reverse shell for port forwarding.
Local / Dynamic Forwarding
On the SSH client
Into the remote
Remote Forwarding
On the SSH server
Back to your system
Here’s how it works:
We compromise CONFLUENCE01
(192.168.125.63
) using CVE-2022-26134.
Our goal is to enumerate PGDATABASE01
's (10.4.125.215
) 5432
port.
A firewall blocks inbound connections.
The only port we can connect is 8090
which forbids us from creating port forwards.
However, CONFLUENCE01
has an SSH client and outbound SSH is allowed.
We can SSH from CONFLUENCE01
to Kali and use remote port forwarding to tunnel database traffic.
Start an SSH server on Kali.
Dynamically access internal services from an external pivot point
Bypass inbound firewall rules that block direct connections
Maintain control by reversing the connection direction (outbound from compromised host)
Remote Dynamic Port Forwarding has only been available since OpenSSH 7.6 (October 2017). The good news? Only the OpenSSH client needs to be v7.6+ — the server version doesn’t matter. In our scenario:
We have CONFLUENCE01
(192.168.125.63
) but we can only talk to it on one port (8090
).
We want to scan other machines on the internal network (like MULTISERVER03
(192.168.125.64
)), but the firewall won’t let our Kali machine directly access them.
From CONFLUENCE01
, we SSH outward to our Kali machine.
We create a SOCKS proxy on our Kali machine that listens on a port (let's say 9998
).
Any traffic sent to this SOCKS proxy, will fly back through the SSH tunnel to CONFLUENCE01
. Then CONFLUENCE01
will forward it to the final destination inside its network.
Start an SSH server on Kali.
Both remote port forwarding and remote dynamic port forwarding use the SSH -R
option; the difference lies in the arguments supplied to -R
:
Classic Remote Port Forwarding
ssh -R [remote_port]:[dest_ip]:[dest_port] user@remote_host
Tunnel to a fixed destination
Remote Dynamic Port Forwarding
ssh -R [remote_port] user@remote_host
Create a SOCKS proxy for dynamic routing
For the remote port forwarding command, if only a port number is provided (e.g.,
-R 9998
), SSH binds the port to the loopback interface (127.0.0.1
) of the SSH server by default.
If needed to terminate a service.