From charlesreid1

What is stunnel

Nominally, stunnel provides SSL encryption and decryption, which provides services not capable of SSL to communicate securely using SSL. (Example: if a mail server listens for unencrypted mail traffic on port 25, and clients send encrypted mail traffic on port 465, stunnel listens on port 465, passes traffic through stunnel to decrypt it, and then passes it to local port 25.

But this can also be used to wrap arbitrary traffic in SSL. In the case of reverse SSH, this provides a way to "wrap" SSH connections in an SSL layer, to make it through the firewall and past intrusion detection systems.

See also RaspberryPi/Reverse SSH

Our stunnel setup will be as follows

The stunnel client will be our Raspberry Pi.

The stunnel server will be our command and control server.

stunnel Server: Command and Control Server


Start by installing stunnel:

$ apt-get install -y stunnel4

Generate Private Keys and Certificates for SSL

Now you need to generate private keys, so that stunnel has private keys to use when encrypting using SSL.

First, go to the directory where stunnel keeps all of its files:

$ cd /etc/stunnel/

Generate Private Key

Use the openssl library to generate a 2048-bit private RSA key:

$ openssl genrsa -out key.pem 2048

Generating RSA private key, 2048 bit long modulus
e is 65537 (0x10001)

Generate a Self-Signed Certificate

To do SSL, an stunnel server must have an SSL certificate, which requires a private key and a signature. We already generated a private key, so now we generate a certificate, and use our own key to sign it. Do this by running the following:

$ openssl req -new -x509 -key key.pem -out cert.pem -days 365

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:.

Key and Certificate

Now you have your private key in key.pem and your server's certificate in cert.pem.

$ ls -lh
total 20K
-rw-r--r-- 1 root root  615 Apr 22  2013 README
-rw-r--r-- 1 root root 1.1K Aug  4 13:06 cert.pem
-rw-r--r-- 1 root root 1.7K Aug  4 13:05 key.pem
-rw-r--r-- 1 root root  208 Aug  4 13:02 stunnel.conf
-rw------- 1 root root 2.8K Aug  4 12:32 stunnel.pem

put those both into the certificate file that we will point to in our stunnel configuration file (next section):

$ cat key.pem cert.pem >> /etc/stunnel/stunnel.pem

Edit Configuration

Now edit the stunnel configuration file on the command and control server, which is located in /etc/stunnel/stunnel.conf:

output = /var/log/stunnel4/stunnel.log
accept =
connect =

where is the ip of the command and control server. This will allow incoming SSL connections to the command and control server on port 443, and will route them to localhost port 22.

Restart stunnel

$ /etc/init.d/stunnel4 start

Open Your Firewall to SSL

Assuming SSH over SSL connections will be coming in through port 443, we should open this on our firewall on our command and control server. This is my script:


iptables -A INPUT -p tcp --dport 443 -j ACCEPT

stunnel Client: Raspberry Pi


Start by installing stunnel on the Pi:

apt-get install -y stunnel4

Copy Private Key

One option, if you want to hack it (this is an insecure option), is to copy the private key from your command and control server to your Raspberry Pi.

A better way to do it would be to generate private keys on both the server and client, and use a level 3 verification in stunnel, whereby both client and server check each others identities. But we're just testing this out.

From the Raspberry Pi, we copy the stunnel private key and certificate from the command and control server at

$ scp /etc/stunnel/stunnel.pem

Edit Configuration

Now edit your stunnel configuration file to resemble the following:

$ cat /etc/stunnel/stunnel.conf 
output 	= /var/log/stunnel4/stunnel.log
cert 	= /etc/stunnel/stunnel.pem
key	= /etc/stunnel/stunnel.pem
pid 	= /var/run/stunnel4/
client  = yes
accept 	=
connect =

Let's break down what that means:

stunnel accepts SSH traffic on port 443. (That means, we have to run the ssh command like ssh -p 443)

It reroutes that SSH traffic through stunnel to wrap it in a layer of SSL encryption. (This is the key to getting past the firewall and IDS).

That encrypted SSL traffic is then sent out to the remote command-and-control server, via port 443. And now there's essentially no difference between how your connection looks and how a standard HTTPS browser session looks. And because it is wrapped in SSL, the firewall/IDS says its ok.

Open Your Firewall to SSL

The last step is to open your client firewall to outgoing SSL connections, using the same script as before:


iptables -A INPUT -p tcp --dport 443 -j ACCEPT

Restart stunnel

$ /etc/init.d/stunnel4 restart

Test It Out

Okay, now give it a shot!

From your client (the Raspberry Pi), run the following command:

$ ssh -p 443 root@localhost

Linux kronos 3.18.0-kali3-amd64 #1 SMP Debian 3.18.6-1~kali2 (2015-03-02) x86_64

The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug  4 13:41:09 2015 from localhost
Cannot open display "default display"

Here's a photo. The Raspberry Pi client is "kali-pi" and the command and control server is "kronos":


When we run ssh -p 443 root@localhost, we're creating "outgoing" SSH traffic (it doesn't actually go out anywhere).

That SSH traffic is routed to port 443, where stunnel is listening. Stunnel wraps the SSH traffic going through port 443 in an SSL layer, than sends that SSL traffic out and over port 443 to our remote command and control server. The remote command and control server, which also has stunnel running, listens on port 443, and when it finds traffic, decrypts it and reroutes it to local host port 22, where it looks like regular SSH traffic.

Reverse SSH Stunnel

This is just part 1 of the trick. Being able to automatically open an SSH tunnel over an arbitrary port is critical to bypassing a firewall, but right now the trick isn't particularly useful, since we aren't usually sitting in front of our Raspberry Pi - we are usually sitting in front of our command-and-control server.

Part 2 of this trick will show you how to set up a reverse SSH connection, so that when the Pi connects to the command-and-control server, it sets up a tunnel that reverse connections can then use. See the RaspberryPi/Reverse SSH Stunnel article.