VPN over DNS

Scot Berner


For some time now, we've been using DNSCat as a means to covertly transmit data during engagements where clients IDS's or Firewalls might otherwise block us.  The DNS protocol is often overlooked by system's administrators and as a result this tool has been immensely useful.

And while there are a other DNS tunneling solutions out there, this is the only one, to our knowledge, that supports 1) encryption 2) a centralized server for simple management 3) Command queuing.  4) Is free.

The one thing it does not support, is the ability to tunnel network traffic, from the client to the server.

The Goal

What if we could setup a bi-directional vpn across dns that would allow all protocols, not just TCP?  This sort of thing is great for situation where you're at an Airport, Hotel, or some other captive portal situation where DNS resolves, but everything else is blocked.  This is also great for penetration testers who want to route/tunnel traffic through system that has been compromised.  And while DNSCat does support tunneling of TCP traffic, its unidirectional. i.e. From the server to the client only (Similar to SSH -L)

The Work Around

Ron (Primary author of DNScat) has mentioned that he intends to build in this feature at some point but for now, lets see if we can hack our way into getting what we want.  To do this we need the following:


For this setup, you will need to register a domain to use. As an example we use mooo.com from freedns.afraid.org. Note that we only care about the NS record which points to our server running the DNScat server software.


On the server side we need to setup a few things. First is to setup ssh keys:


Next, we enable routing on the server:

echo 1 > /proc/sys/net/ipv4/ip_forward

Next we run we run the DNSCat Server.  Lets go through some of the switches we used:

  •  The -c switch is the preshared crypto key we want to use between the client and the server. This is optional, since we're going to be running a vpn across this, but why not add the extra layer of encryption.
  •  The -u switch tells the server to automatically attach to each inbound dnscat client session.
  •  The -a switch tells the server that we want to automatically run the following command on each new session.  As per the documentation, the 'listen' command will establish a tunnel between the server and the client where on the server a listening socket is created on port 2222 and all connections are forwarded to the client to  This could easily be changed to forward browser traffic to www.google.com by changing it to read 'listen www.google.com:80'.

After the server is started, we switch to the client.


We wont go into to details on how to compile the client, you can find those instructions on the github/readme page.  However for this to work, we compile the client and run it locally as root. But before we do that we need to make some modifications to the sshd config. Three changes need to be made to the standard sshd_config

  1. Comment out 'PermitRootLogin without-password'
  2. Add 'PermitRootLogin yes'
  3. Add 'PermitTunnel yes'

Next restart the service and enable IP routing

# /etc/init.d/ssh restart
# echo 1 > /proc/sys/net/ipv4/ip_forward

Once forwarding is enabled, we can connect our client to the server.

We can see that the session has been established, and on the server we see the following:

Next step is to push our keys from the server to the client. To do this, on the server run the following command:

scp /root/.ssh/id_rsa.pub [email protected]:/root/.ssh/authorized_keys -P 2222

Note, when being prompted for creds, this is the root password on the client machine.

Once the server ssh key has been pushed to the client. Now establish a SSH based vpn tunnel (-w) from the server to the client.

ssh -i /root/.ssh/id_rsa [email protected] -p 2222 -w 1:1 -o TCPKeepAlive=yes -o ServerAliveInterval=50 &

If successful, you should now have an interface tun1 on both the client and the server. On both machines you'll need to provision IP's, Routes and IPTables for natting.

On Server

ifconfig tun1 address netmask
ifconfig tun1 up
route add [client network]/[netmask] via dev tun1
iptables -t nat -A POSTROUTING -o tun1 -j MASQUERADE

On client (via SSH):

ssh -i /root/.ssh/id_rsa [email protected] -p 2222 'ifconfig tun1 address netmask'
ssh -i /root/.ssh/id_rsa [email protected] -p 2222 'ifconfig tun1 up'
ssh -i /root/.ssh/id_rsa [email protected] -p 2222 'route add [server network]/[netmask] via dev tun1'
ssh -i /root/.ssh/id_rsa [email protected] -p 2222 'iptables -t nat -A POSTROUTING -o tun1 -j MASQUERADE'

So now, traffic from the server or the client should be able to reach either sides network. If you want to forward all traffic out, you could even put in a default route.

Final Thoughts

This solution, while effective, is slow. Not to mention, this will only work on *nix systems. But on the plus side, all TCP, UDP and ICMP traffic is properly routed across the tunnel allowing for such things as full port scans and streaming Netflix.


Are you ready to start your technology journey? The friendly experts at SynerComm are here to help.

From design to deployment to troubleshooting and everything in between, the friendly experts at SynerComm are always here to help.
linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram