What if SSH could VPN?
Updated: Apr 28, 2021
There is a handy tool that can allow anyone with a user level unprivileged SSH connection to create a VPN tunnel with little overhead. Common use cases are found within security assessments and penetration tests. The context is simple, you have SSH access to an internal machine which happens to have access to other subnets. Perhaps you are unable to weaponize the target machine for what ever reason and this is where SSHUTTLE comes in handy. Take a look at the basic diagram below.
You can see in this straight forward example above, that the Firewall allows only SSH traffic, so by using sshuttle we can exploit this and send TCP streams through the firewall, encapsulated in the SSH traffic for disassembly into packets on the target webserver with headers pointing to the target subnet. This is in a nutshell, what is happening when we pivot to a non routable subnet with sshuttle.
A VPN or a PortForward?
The short answer is neither, it cannot be classified as either or both.
it can be viewed partially as a VPN as it forwards not just a specified port, but all ports on an entire network. From another perspective it can be viewed as SSH port forwarding. A VPN would forward a packet one at a time and not concern itself with individual connection, being stateless in regards to traffic. sshuttle on the other hand is not stateless and will track all connections.
SSHUTTLE and TCP
There can be some confusion here when working with TCP traffic over an established sshuttle session. Although in some cases TCP tools may work, many wont due to the stateless nature of transporting TCP data over ssh. At a fundamental level, TCP relies on packet confirmation and packet loss via SynACK. Well, this cannot be done over sshuttle the same way a normal TCP session would be established. Sshuttle assembles the TCP stream locally, and then transports it over a stateful SSH connection and dissembles the packets locally so it never does TCP over TCP, it is instead doing data over TCP.
On the host side of things, the sshuttle listens on a port creating an iptable NAT rule to direct all the traffic into a specified port. The following iptables chain is created in a NAT table
$ iptables -t nat -N sshuttle-12300
At the head of the "PreRouting" and "OutPut" chains, we have two rules.
Do nothing for tcp traffic destined to localhost -j RETURN --dest 127.0.0.1/32 -p tcp
Redirect tcp traffic into the locally listening sshuttle port. -j REDIRECT --dest 0.0.0.0/0 -p tcp --to-ports 12300 -m ttl ! --ttl 42
In the sshuttle docs, there is an option to specify the binding of this port. You can make it listen on a particular interface, or listen on all, or the default, listen on 127.0.0.1.
In a pinch you could have sshuttle running on a box, open the port to a particular interface.
There is a semi alternative that exists through the use of an SSH bind and a SOCKS5 proxy to enable forwarding traffic over SSH. This big difference here however, is you are not forwarding all traffic on the local device, you have to target a specific port defined in your bind and socks5 proxy chain configuration file. Leveraging SSH forwarding can save allot of time compared to utilizing plink or chisel in the sense that you do not have to worry about a remote file upload and execution, which is often not possible during a penetration test or horizontal movement without setting of some type of alert or blip on the radar for a lack of terms. This is definitely the preferred method in any situation where you may want to live off the land, and have access to a linux workstation or smart device. Yes I did say smart device, from experience I have utilized sshuttle and proxy chains with a bound SSH connections to launch various attacks from a smart thermostat over a non routable subnet to pivot internally to the target network.
Common Use Cases
Your client machine (or router) is Linux, FreeBSD, or MacOS.
You have access to a remote network via ssh.
You don't necessarily have admin access on the remote network.
The remote network has no VPN, or only stupid/complex VPN protocols (IPsec, PPTP, etc). Or maybe you are the admin and you just got frustrated with the awful state of VPN tools.
You don't want to create an ssh port forward for every single host/port on the remote network.
You hate openssh's port forwarding because it's randomly slow and/or stupid.
You can't use openssh's PermitTunnel feature because it's disabled by default on openssh servers; plus it does TCP-over-TCP, which has terrible performance.
$ sudo pip install sshuttle # you may need to install # python-pip
2. Tunnel Traffic
There are two ways to do this, the first is the most generic way which I steer away from. Reason being, ssh connections can reprompt for credentials depending on the permissions of the local box, and you have no way of noticing these prompts or passing credentials in some instances. But generally speaking it can just be a pain.
$ sudo sshuttle -r user@server "target IP"
The second method is my preffered method and involves storing the credentials persistently for rapid use with the "sshpass" command being passed prior to sshuttle connection.
$ sudo sshpass -p "password" sshuttle -r user@IPaddress "Target subnet" -vv
From here we have several options for customizing and configuring this process.
Force all dns requests through the tunnel
Run as a daemon.
Customize the ssh command with a ssh keypair for example.