Port forwarding with SSH

Port forwarding is a foundational skill for any penetration tester or red teamer. This article covers the basics and discusses a few practical examples of port forwarding with SSH.

Table of Contents

Port forwarding and SSH

Port forwarding is a technique that redirects data from one port to another through a tunnel. Using port forwarding, we can, for example, access an internal web portal running behind a firewall or expose an internal database to a trusted client.

SSH is often used to establish these tunnels since it is available universally across Unix systems and handles user authentication, data secrecy, and data integrity. In the following sections, we go through three types of port forwarding supported by SSH:

  1. Local port forwarding
  2. Remote port forwarding
  3. Dynamic port forwarding.

Local port forwarding

Local port forwarding is used to access a service on a remote server that is not accessible directly from the local server. For example, we may use it to access an internal website listening in a private subnet or bypass a restrictive firewall that allows access only from specific hosts. When using local port forwarding, a new port is opened locally on the side from which we connect. Traffic coming to the opened port is forwarded through the SSH tunnel to the remote service.

# connections to 127.0.0.1:3306 will be forwarded to target:3306
ssh -L 127.0.0.1:3306:127.0.0.1:3306 user@target

# connections to 127.0.0.1:3306 will be forwarded to 10.0.0.1:3306
# in the target's local network
ssh -L 127.0.0.1:3306:10.0.0.1:3306 user@target

As you can see in the second example, we can use forwarding to access the host and any other host in its network or the Internet.

Remote port forwarding

Remote port forwarding is used to expose a local service to a remote client. For example, we may use it to expose a database to a remote server. When using remote port forwarding, a new port is opened remotely on the server we connect to. Traffic coming to the remote port is forwarded to the local machine. Remote port forwarding is sometimes called reverse port forwarding because the data flows in the reverse direction to the established SSH connection.

# connections to target:8080 will be forwarded to 127.0.0.1:80
ssh -R 8080:127.0.0.1:80 user@target

When using remote port forwarding, we allow anyone on the server to connect to our local machine.

Dynamic port forwarding

Although remote and local port forwarding rules are handy and multiple forwarding rules can be enabled by repeating the flags, listing all specific ports in advance becomes tiresome very quickly.

We use dynamic port forwarding to access a broader range of hosts and ports. Dynamic port forwarding opens a SOCKS proxy. SOCKS client can connect to the proxy and route traffic to arbitrary servers and ports on the other side. The socks proxy can be opened locally with -D or remotely with -R.

# local SOCKS proxy
ssh -D1080 user@target

# remote SOCKS proxy
ssh -R1080 user@target

Many utilities support SOCKS proxy by default, e.g., your web browser or command-line utilities (e.g., curl). For utilities that don’t support this, we can use proxychains.

Remarks and clarifications

There are a few limitations to be aware of. Firstly, it is not possible to forward UDP traffic through SSH tunnels. Other tools have to be used for this use case, such as socat or chisel.

Secondly, SSH is mainly useful for Unix machines and will be of little use in Windows domains. Chisel, as mentioned above, can be used for these purposes.

Lastly, there is a feature of SSH I still need to speak about: Unix domain sockets. Unix domain sockets are particular files used on Unix systems for inter-process communication. SSH can be used to forward traffic between two sockets on different servers. We can use these to, for example, interact with a database or a Docker daemon running on the remote server. The usage is simple; specify the filename instead of the host and port.

# create ~/docker.sock locally and forward traffic to /var/run/docker.sock on
# the target
SSH -L ~/docker.sock:/var/run/docker.sock user@target

SSH forwarding and red-team operations

Suppose the following situation: We have achieved code execution on the machine and may even have shell access, but we don’t yet have a set of credentials with which to log in. We cannot use local port forwarding to access the machine without logging in!

There may be a solution, though - if we connect from the machine to our server and set up remote port forwarding, we can access the internal network. But is it okay to allow connection from an untrusted environment to our servers? Aren’t we walking into a situation where threat actors may steal our credentials and do bad things to our machine? Thankfully, SSH can be configured so that remote clients are not allowed to execute any commands and cannot even access the internal network. The only thing the clients can do is connect and expose their network to us. This fits our use case perfectly.

I have created a Docker container with SSH configured for this specific situation. After achieving code execution, I can start a new SSH session to my container:

# creates reverse SOCKS proxy
ssh -R 3128 -N guest@127.0.0.1

I can now access the network I am connecting from. The user guest on the container has an empty password, so no user interaction is required. Because code execution and local port forwarding are disabled, it does not matter if someone successfully logs in with the same user:

ssh guest@127.0.0.1 cat /etc/passwd
# channel 0: open failed: connect failed: open failed

ssh -NL 8080:127.0.0.1:8080 guest@127.0.0.1
# channel 2: open failed: administratively prohibited: open failed

We can monitor these failed attempts as they are logged in the journal:

2024-05-20T22:03:57.640426+00:00 839edcf3582c sshd[259]: error: no more sessions
2024-05-20T22:05:16.927142+00:00 839edcf3582c sshd[265]: refused local port forward: originator ::1 port 41210, target 127.0.0.1 port 8080

Take a look at my GitLab repository to try it out for yourself!

Summary

In this article, we explained the basics of SSH port forwarding, mentioned the limitations of SSH and the workarounds, and finally explored remote port forwarding to gain access to the remote networks in CTFs and red-team operations.