SSH Port Forwarding Explained

SSH Tunneling and pivoting are well-known by Penetration testers and cybersecurity professionals. In some cases, network engineers, system administrators and developers typically benefit from SSH tunnelling.

Please note that the commands provided in this article are designed for Linux environments. Modifications may be required for them to work in Windows.

Benefits

SSH port forwarding can be useful in various scenarios. Here are some:

Secure Browsing: If you’re using an insecure network, you can use SSH port forwarding to tunnel your web traffic which will encrypt your data, protecting it from anyone snooping on the network.

Bypassing Firewalls: If certain services or sites are blocked on your network, you can forward your traffic to an SSH server elsewhere to bypass the restrictions.

Remote Access to Services: If a service is running on a machine that is not publicly accessible (like a database running in a private network), you can use SSH port forwarding to access the service from your local machine securely.

Testing: You can use port forwarding to test how an application responds to requests from different locations or IPs, which can be beneficial when diagnosing network or firewall issues.

File Transfers: SSH port forwarding can provide a secure tunnel for transferring files between machines. It is handy when dealing with sensitive data.

Risks

While SSH port forwarding can be beneficial, it also comes with potential risks and issues:

Security Risks: The primary risk associated with SSH port forwarding is that it could be exploited by an attacker if your SSH keys are compromised. An attacker could forward ports to sensitive internal services, bypassing firewall protections.

Misconfiguration Issues: Port forwarding relies on correct configuration. Misconfiguration can send data to the wrong place, potentially causing data loss or exposure.

Bypassing Network Policies: As port forwarding can be used to bypass network restrictions, it might allow users to access content or services that are usually blocked, which could lead to violations of corporate policies or even legal regulations.

Performance Impact: All the traffic in SSH port forwarding goes through the SSH connection, which can significantly load the network and degrade performance.

Port Forwarding Explained

There are three types of SSH port forwarding – local, remote, and dynamic.

Local SSH Port Forwarding (Outgoing SSH tunnel): This is the most common type of SSH port forwarding. In local port forwarding, connections from an SSH client are forwarded via the SSH server to a destination server or port. This type of forwarding can be used to secure a connection to a remote service, like a database, that doesn’t natively support encrypted connections. The client cannot connect to the destination if the remote server is behind a firewall that blocks SSH traffic. That is why Remote SSH Port Forwarding is more useful in some cases.

Remote SSH Port Forwarding (Incoming SSH tunnel): This works opposite to local port forwarding. Remote port forwarding is established from the server machine to the client machine. 

Dynamic SSH Port Forwarding (Dynamic tunnelling): In dynamic port forwarding, an SSH server functions as a SOCKS proxy server, which can accept connections on any port and forward them to various destinations. This method doesn’t require a destination IP and port at the setup time. Instead, it determines the destination for each packet based on the packet’s content, allowing for a much more flexible and powerful proxying solution.

These methods can provide secure, encrypted tunnels for your data, but each is suited to slightly different use cases. Now let’s look at how to configure them. 

How to configure Local Port Forwarding

When you set up local port forwarding, any data sent to the specified local port on your machine gets encrypted and transmitted through the SSH tunnel to the remote server. The encrypted data is decrypted on the server side, and the SSH server forwards it to the specified destination port. This way, you can interact with applications and services running on the remote server as if on your local machine.

For example, let’s say you have a Postgre SQL server running on a remote server at IP address `target_IP`, and it listens on port 5432 for SQL requests. You can use local port forwarding to forward incoming traffic from port 9999 on your local machine to port 5432 on the remote server. 

Here’s the command to set up the local port forwarding used in pen-testing scenarios where an attacker (pentester in our case) is trying to reach a remote service:

ssh -N -L [bind_IP:]attacker_port:target_IP:target_port ssh_username@target_host

In the simplified case shown in the photo, the command would look like the following: 

ssh -N -L 0.0.0.0:9999:10.2.2.1:5432 ssh_username@203.0.113.1

If the app client is on the same host as the SSH client, use the below command: 

ssh -N -L 9999:localhost:5432 guest@remote_server_ip

Command breakdown:

-N: Do not execute any remote command when established. 
-L: We specify the user to log in on the remote machine for local port forwarding.
attacker_IP: This is optional and only needs to specify a source. It could be 0.0.0.0 if we wish to use any node from the attacker’s local network to be able to use it. We don’t need to set this option when our source IP is localhost, i.e. when the app client is on the same machine as the ssh client. 
attacker_port: This local port on the local attacker machine will be tunnelled to the SSH server’s destination. In our example, anything we send to the local port 9999 on the attacker’s computer will be tunnelled through the SSH link and exit the ssh server on the remote end. 
target_IP: this is the IP address of the destination node from the perspective of the destination SSH server output (the tunnel output). If the target is the same as the ssh server, we could use localhost here. But we can provide its IP address if it’s a different node.
ssh_username@targethost: username and IP address of target ssh server on the destination node. This could be any available user, such as an unprivileged user as guest@remote_server_ip. But note, though, that if the user doesn’t have root privileges, ports 0 to 1024 can not be used for tunnelling. 

After establishing the SSH connection, you can use the following command to connect to the remote Postgres SQL server. 

Our App Client Example:

 psql -h localhost -p 9999 -U postgres

Local port forwarding is beneficial when you want to access services on a remote server securely or when the services on the remote server are not directly accessible from your local network due to firewall restrictions or other security concerns. It provides a convenient and secure way to interact with remote resources without exposing them directly to the internet or compromising security. However beneficial, malicious actors can also use it for various intrusions.

How to Configure Remote Port Forwarding

Remote port forwarding is similar to local port forwarding, but there’s a significant distinction. The tunnel originates, in reverse, from the remote host and connects to the local host. To clarify with an example, consider a penetration testing scenario. An attacker exploits a web application vulnerability and gains shell access to a remote host. This remote host sits behind a firewall and lacks an SSH server. Consequently, local port forwarding won’t be feasible; the attacker can’t establish a connection from their local system to the remote one. However, the attacker can use the remote SSH client to create a reverse tunnel that connects to their local machine.

Here’s the command to set up the remote port forwarding used in pen-testing scenarios where an attacker (pentester in our case) is trying to reach a remote service:

ssh -N -R [attacker_IP:]attacker_port:target_IP:target_port ssh_username@target_host

In the simplified case shown in the photo, the command would look like the following: 

ssh -N -R 192.51.100.1:9999:10.2.2.1:5432 ssh_username@192.51.100.1

Keep in mind the critical distinctions from local port forwarding:

  1. The switch ‘-L‘ is replaced with ‘-R‘.
  2. Since the connection is initiated in reverse, the attacker’s SSH username and password are required from the remote host (acting as the SSH client) to the attacker’s computer (serving as the SSH server).
  3. Attacker-IP is now the IP address of the SSH server on the attacker’s computer.

Command breakdown:

-N: Do not execute any remote command when established. 
-R: We specify the user to log in on the remote machine for remote port forwarding.
attacker_IP: This is the public or accessible IP address on the attacker’s computer, our SSH server.
attacker_port: This local port on the local attacker machine will be tunnelled to the remote. In our example, anything we send to the local port 9999 on the attacker’s computer will be tunnelled through the SSH link and exit the ssh client on the remote end. 
target_IP: this is the IP address of the destination node from the perspective of the destination SSH server output (the tunnel output). If the target is the same as the ssh server, we could use localhost here. But we can provide its IP address if it’s a different node.
ssh_username@attacker_ip: username and IP address of target ssh server on the attacker node. 

After establishing the SSH connection, you can use the following command to connect to the remote Postgres SQL server. 

Our App Client Example:

 psql -h localhost -p 9999 -U postgres

Dynamic Port Forwarding (SOCKS Proxy):

Dynamic port forwarding, also known as dynamic SOCKS proxying, is another feature of SSH that allows you to create a secure and encrypted tunnel between your local computer (client) and a remote server. Unlike local and remote port forwarding, which forwards traffic from a specific local port to a particular remote destination, dynamic port forwarding sets up a SOCKS proxy on your local machine to route traffic from various applications through the SSH tunnel to the remote server.

Here’s how dynamic port forwarding works:

Setting up Dynamic Port Forwarding: To initiate dynamic port forwarding, you establish an SSH connection with dynamic port forwarding enabled. The following command accomplishes this:

ssh -N -D bind_IP:bind_port username@remote_server_ip

Example:

ssh -N -D localhost:8080 username@remote_server_ip

This command establishes an SSH connection with the remote server (`remote_server_ip`) using the specified username (`username`). It sets up a SOCKS proxy on your local machine, listening on port 8080.

Configuring Applications: Once the dynamic port forwarding is set up, you need to configure your applications to use the SOCKS proxy at `localhost:8080` (or the port you specified). Most modern web browsers and many other applications allow you to configure proxy settings.

Routing Application Traffic: With the SOCKS proxy configured, any network traffic generated by applications that use the proxy will be encrypted and forwarded through the SSH tunnel to the remote server. The remote server then sends the traffic to the respective destinations as if it originated from the server itself.

Dynamic port forwarding is particularly useful when you route traffic from multiple applications or access the internet from a restricted network environment. Using the SSH tunnel as a SOCKS proxy, you can bypass firewalls and access resources that might be otherwise blocked.

Dynamic port forwarding provides a flexible and secure way to route traffic from various applications through the SSH tunnel, allowing you to access remote resources and bypass restrictions without compromising security.

Closing the Tunnel

To close the SSH tunnel, terminate the SSH connection by typing `exit` in the terminal or pressing `Ctrl + C`.