SSH Tunneling

Introduction

Tunneling describes the act of encapsulating one kind of data stream within another as it travels across a network. Certain protocols called tunneling protocols are designed specifically to do this. Secure Shell (SSH) is an example of one of these protocols.

In the background of each SSH connection, all shell commands, passwords, and data are transported through an encrypted tunnel built using the SSH protocol. The SSH protocol is primarily a tunneling protocol, so it's possible to pass almost any kind of data through an SSH connection. For that reason, tunneling capabilities are built into most SSH tools.

Another great benefit of SSH tunneling is how its use can easily blend into the background traffic of network environments. SSH is used often by network administrators for legitimate remote administration purposes, and flexible port forwarding setups in restrictive network situations. It's therefore common to find SSH client software already installed on Linux hosts, or even SSH servers running there. In network environments that are not heavily monitored, SSH traffic will not seem anomalous, and SSH traffic will look much like regular administrative traffic. Its contents also cannot be easily monitored.

In most official documentation, tunneling data through an SSH connection is referred to as SSH port forwarding.

SSH Tunneling Scenario

This scenario uses context from the Port Forwarding on Linux scenario.

In this scenario, socat is no longer available on CONFLUENCE01. We still have all the credentials we previously cracked from the Confluence database, and there is still no firewall preventing us from connecting to the ports we bind on CONFLUENCE01.

The PGDATABASE01 is attached to another internal subnet. We find a host with a SMB server port open on TCP port 445 in that subnet. We want to be able to connect to that server and download what we find to our local attacking machine.

In this type of scenario we need to create an SSH local port forward as part of the SSH connection from CONFLUENCE01 to PGDATABASE01. We will bind a listening port on the WAN interface of CONFLUENCE01. All packets sent to that port will be forwarded through the SSH tunnel. PGDATABASE01 will then forward these packets toward the SMB port on the new host we found.

In this diagram, we listen on TCP port 4455 on CONFLUENCE01. Packets sent to CONFLUENCE01 are pushed by the SSH software through the SSH tunnel. At the other end of the tunnel, the SSH server software on PGDATABASE01 forwards them to TCP port 445 on the newly-found host.

Now we can connect to the SMB port from our attackbox

Scripts used

Port Scanning Script with NC

SSH Dynamic Port Forwarding

Local port forwarding has a glaring limitation of only connecting to one socket per SSH connection. This makes it quite tedious to use at scale. Luckily, OpenSSH also provides dynamic port forwarding. From a single listening port on the SSH client, packets can be forwarded to any socket that the SSH server host has access to. The only limitation is that the packets have to be properly formatted - most often by SOCK-compatible client software.

This means we will still be able to access the SMB port on HRSHARES, but we can also access any other port on any other host that PGDATABASE01 has access to, through this single port. However, in order to take advantage of this flexibility, we need to ensure that whatever software we use can send packets in the correct SOCKS protocol format.

Let's extend the previous scenario. As well as connecting to the SMB port on HRSHARES, we also want to be able to do a full portscan of HRSHARES.

OpenSSH Dynamic Port Forwarding

In OpenSSH, a dynamic port forward is created with the -D option. The only argument it takes is the IP address and port we want to bind to. In this example we will listen on all interfaces on port 9999. We'll also pass the -N flag to prevent a shell from being spawned.

If we were to try to connect to the SMB port 445 on the HRSHARES using our new dynamic port forwarding, we would find that smbclient does not natively provide an option to use a SOCKS proxy. Without a native option to use a SOCKS proxy in smbclient, we can't take advantage of our dynamic port forward.

Using Proxychains to Use SOCKS Proxies

Proxychains is a tool that can force network traffic from third party tools to over HTTP or SOCKS proxies.

Proxychains uses a configuration file for almost everything, stored by default at /etc/proxychains4.conf. By default, proxies are defined at the end of the file. We can simply replace any existing proxy definition in that file with a single line defining the proxy type, IP address, and port of the SOCKS proxy running on CONFLUENCE01 (socks5 <ip> 9999).

Although we specify socks5 in this example, it could also be socks4, since SSH supports both. SOCKS5 supports authentication, IPv6, and User Datagram Protocol (UDP), including DNS. Some SOCKS proxies will only support the SOCKS4 protocol.

Must use sudo with proxychains

SSH Remote Port Forwarding

In our examples so far, we've been able to connect to any port we bind on the WAN interface of CONFLUENCE01. But in the real world this is more challenging because firewall hardware and software is frequently employed and inbound traffic is often controlled much more aggressively than outbound traffic. Only in rare cases will we compromise credentials for an SSH user, allowing us to SSH directly into a network and port forward. We will only very rarely be able to access ports that we bind to a network perimeter.

SSH remote port forwarding can be used to connect back to an attacker-controlled SSH server, and bind the listening port there. We can think of it like a reverse shell, but for port forwarding.

While in local and dynamic port forwarding, the listening port is bound to the SSH client, in remote port forwarding, the listening port is bound to the SSH server. Instead of the packet forwarding being done by the SSH server, in remote port forwarding, packets are forwarded by the SSH client.

We can connect FROM CONFLUENCE01 to our attack machine over SSH. The listening TCP port 2345 is bound to the loopback interface on our attackbox. Packets sent to this port are pushed by our attackbox SSH server through the SSH tunnel back to the SSH client on CONFLUENCE01. They are then forwarded to the PGDATABASE01 machine.

Setup the SSH server on our attackbox:

Check to make sure the SSH port is open:

In order to connect back to our SSH server using a username and password, you may have to explicitly allow password-based authentication by settings PasswordAuthentication to yes in /etc/ssh/sshd_config.

The SSH remote port forward option is -R, and has a similar syntax to the local port forward option. It also take two socket pairs as the argument. The listening socket is defined first, and the forwarding socket is second.

In this case we want to listen on port 2345 on our attack machine (127.0.0.1:2345) and forward all traffic to the PostgreSQL port on PGDATABASE01 (10.4.230.215)

We can confirm that our remote port forward port is listening by checking if port 2345 is open on our Kali loopback interface.

We can now start probing port 2345 on the loopback interface of our Kali machine, as though we're probing the PostgreSQL database port on PGDATABASE01 directly

SSH Remote Dynamic Port Forwarding

With remote port forwarding, we were able to forward packets to one socket per SSH connection. However, just as we found with local port forwarding, this single-socket-port-connection limitation can slow us down. We often want more flexibility when attacking networks, especially in the enumeration stages.

Luckily, remote dynamic port forwarding con provide this flexibility. Just as the name suggests, remote dynamic port forwarding creates a dynamic port forward in the remote configuration. The SOCKS proxy port is bound to the SSH server, and the traffic is forwarded from the SSH client.

Remote dynamic port forwarding is just another instance of dynamic port forwarding, so we gain all the flexibility of traditional dynamic port forwarding alongside the benefits of the remote configuration. We are able to connect to any port on any host that CONFLUENCE01 has access to by passing SOCKS-formatted packets through the SOCKS proxy port that is bound on our attackbox.

Remote Dynamic Port Forwarding Scenario

Let's extend our scenario. This time we find a Windows server (MULTISERVER03) on the DMZ network. The firewall prevents us from connecting to any port on MULTISERVER03, or any port other than TCP/8090 on CONFLUENCE01 from our attack machine. We can still SSH out from CONFLUENCE01 to our attackbox, then create a remote dynamic port forward so we can start enumerating MULTISERVER03 from our attackbox.

The SSH session is initiated from CONFLUENCE01, connecting to the Kali machine, which is running an SSH server. The SOCKS proxy port is then bound to the attack machine on TCP/9998. Packets sent to that port will be pushed back through the SSH tunnel to CONFLUENCE01, where they will be forwarded based on where they're addressed - in this case, MULTISERVER03.

The remote dynamic port forwarding command is relatively simple, although it uses the same -R option as classic remote port forwarding. The difference is that when we want to create a remote dynamic port forward, we pass only one socket: the socket we want to listed on the SSH server. We don't even need to specify an IP address; if we just pass a port it will be bound to the loopback interface of the SSH server by default.

make sure the port is bound on our local attackbox

Just as with standard dynamic port forwarding, we use Proxychains to tunnel traffic over this SOCKS proxy port. We'll edit our Proxychains configuration file at /etc/proxychains4 on the local machine to reflect the new SOCKS proxy port.

Using sshuttle

In situations where we have direct access to an SSH server, behind which is a more complex internal network, classic dynamic port forwarding might be difficult to manage. sshuttle is a tool that turns an SSH connection into something similar to a VPN by setting up local routes that force traffic through the SSH tunnel. However, it requires root privileges on the SSH client on Python3 on the SSH server, so it's not always the most lightweight option.

Last updated