Some Notes on DNS Tunnels

July 17, 2017

I’ve been spending a decent number of frustrating evenings fighting with DNS tunnels and wanted to write a quick post to capture my thoughts while they were fresh in my head.

DNS Recursion

DNS tunnels work through the magical properties of DNS recursion. Without getting too far into it, here’s how that works:

  1. You ask your local DNS server if it knows where you can find foo.bar.mydomain.com. It has no idea. If it is not configured for recursion, and most of the ones I’ve seen are, it sends a big fat nope to you and that’s that. Otherwise, one of two things will occur.
  2. A) It will forward your request to another DNS server or B) It will forward it to the root DNS servers. Since option A) eventually ends up in step 1 or option B, we’ll move forward.
  3. The root servers contain a list of all top-level domains (.com, .net, .ninja, etc.). The root name servers will have no idea where foo.bar.mydomain.com is, but they do know which server is responsible for .com, so it helpfully forwards your request there
  4. The .com name servers receive the request and similarly have no idea where foo.bar.mydomain.com is, but they do know of a NS record for mydomain.com. It forwards your request there.
  5. The name server for mydomain.com receives the query and looks at the list of zones it is authoritative for and see that it, too, doesn’t know much about foo.bar.mydomain.com, but it does know where to find the name server for bar.mydomain.com and passes your query along.
  6. Finally your DNS query arrives at the name servers for bar.mydomain.com. Since they’re authoritative for the zone, they know exactly where to find foo.bar.mydomain.com and sends a response, allowing you to do whatever it is that you were doing to a hostname. Great.

Tunneling Through DNS

If we keep the above example in mind, let’s imagine that you were on a locked down network with a recursive DNS server and I was in possession of the bar.mydomain.com DNS server.

If you wanted to send me a message, you could just do something like this:

dig hi-liam.bar.mydomain.com

and I would see a DNS query come in for hi-liam. I could then send a response using a TXT record that says something like ‘sup?’. You could then respond with a:

dig oh-not-much.bar.mydomain.com

And we’d have a painfully slow but pretty stealthy conversation. DNS tunnelling works more or less the same way, but is automated.

So this seems pretty neat, right? We’ve got a covert comms channel baked into the architecture of the internet that will almost always be working.

The cake is a lie, man.

You see, not really, because DNS tunnels exist in this strange limbo where they’re feasible enough to exist, but not widely useful enough to actually have solid solutions in place.

You’re also swimming upstream against some decent technical restrictions imposed on us as a result of us co-opting the backbone of the internet for covert C2 traffic.

The Contenders

I tested out four DNS tunnels with varying degrees of success. I stuck my DNS server in Azure since I’ve got credits fo’ DAYS, and configured a name server for a $1 domain I’d purchased.

Here’s the DNS configuration I used for the mydomain.com domain:

@ 10800 IN SOA ns1.gandi.net. hostmaster.gandi.net. 1496496973 10800 3600 604800 10800
ns1 300 IN A <DNS SERVER IP>
tunnel 300 IN NS ns1.mydomain.com.

Let’s unpack what I’ve just done. The ns1 A record is just a normal host record that tells us where to find a host. In my previous example, this would be foo in foo.bar.mydomain.com.

The next line is where the magic happens. What we’re doing here is defining name servers for the tunnel.mydomain.com domain (or bar.mydomain.com domain if you’re stuck on the previous example still). Our messages will be sent in the form <message text>.tunnel.mydomain.com.

The tunnel part names the subdomain, and the ns1.mydomain.com tells us where to find the DNS servers for that domain. In practice, these are usually in different zones. Here’s what the NS records for smallsec.ca look like:

; <<>> DiG 9.8.3-P1 <<>> ns smallsec.ca
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46359
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;smallsec.ca.			IN	NS

;; ANSWER SECTION:
smallsec.ca.		86277	IN	NS	dora.ns.cloudflare.com.
smallsec.ca.		86277	IN	NS	sam.ns.cloudflare.com.

;; Query time: 48 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Thu Jun  8 06:14:48 2017
;; MSG SIZE  rcvd: 83

These NS records tell us where to send queries for the domain in question.

Iodine

I got Iodine up and running with minimal fuss, but it was slow as fuck. One neat thing about it is that it tunnels IP, so you can route through it if you like, making this totally transparent to the tools you’re using.

First we start the server:

[email protected]:~$ sudo iodined  -f -DD -c -4 -P secretpassword123 10.10.10.1 bar.mydomain.com
Debug level 2 enabled, will stay in foreground.
Add more -D switches to set higher debug level.
Opened dns0
Setting IP of dns0 to 10.10.10.1
Setting MTU of dns0 to 1130
Opened IPv4 UDP socket
Listening to dns for domain bar.mydomain.com

Then the client:

[email protected]:~$ sudo iodine -I 1 -L 1 -f -P secretpassword123 bar.mydomain.com

Here’s what the initial communication looks like:

RX: client 8.8.8.8, type 10, name yrb54k.bar.mydomain.com
TX: client 8.8.8.8, type 10, name yrb54k.bar.mydomain.com, 48 bytes data
<snip>
RX: client 8.8.8.8, type 10, name paaap5za.bar.mydomain.com
PING pkt from user 0, ack for downstream 0/0
RX: client 8.8.8.8, type 10, name paaap5zi.bar.mydomain.com
PING pkt from user 0, ack for downstream 0/0

This shows the first lookup (foo.bar.mydomain.com from our first example) and then I’ve cut out the client/server negotiation. The last two RX/PING pairs are just keepalives. Neat!

DNS2TCP

This… didn’t work. A tunnel would start, then disconnect and refuse to connect again. I don’t know why, but I discovered dnscat2 before I found a solution and then I no longer cared.

TUNS

I read about TUNS and got really excited, I think mainly because I’d been swearing at DNS2TCP for days. The author wrote a paper on the topic of DNS tunneling, which is definitely worth a read, that introduces the tool. Here’s the part that made me giddy:

an IP over DNS tunnel, focused on simplicity and protocol compliance. Comparison of TUNS and the other implementations showed that this approach is successful: TUNS works on all the networks we tested, and provides reasonable performance despite its use of less efficient encapsulation techniques, especially when facing degraded network conditions

dnscat2

I kept coming back to DNS2TCP for some reason and reached out to a colleague for help who sent me to dnscat2. I’ll let the author speak for himself:

dnscat2 strives to be different from other DNS tunneling protocols by being designed for a special purpose: command and control.

This isn’t designed to get you off a hotel network, or to get free Internet on a plane. And it doesn’t just tunnel TCP.

It can tunnel any data, with no protocol attached. Which means it can upload and download files, it can run a shell, and it can do those things well. It can also potentially tunnel TCP, but that’s only going to be added in the context of a pen-testing tool (that is, tunneling TCP into a network), not as a general purpose tunneling tool. That’s been done, it’s not interesting (to me).

It’s also encrypted by default. I don’t believe any other public DNS tunnel encrypts all traffic!

This one gets my vote. It’s also somehow faster than the others, which was a nice surprise.

Conclusion

There’s a lot to like about DNS tunnels. I love that they’re built into the infrastructure. I love that recursion is almost always enabled and provides a way for this traffic to sneakily exit the network. There are almost certainly ways to detect and block this traffic, but I think it’s unlikely most sites will implement them in the near future.

References

DNS2TCP

Iodine

dnscat2

TUNS

Misc DNS Tunneling Resources