It's no secret that I love DNS. It's an awesome protocol. It's easy to understand and easy to implement. It's also easy to get dangerously wrong, but that's a story for last week a few weeks ago. :)

I want to talk about interesting implication of DNS's design decisions that benefit us, as penetration testers. It's difficult to describe these decisions as good or bad, it's just what we have to work with.

What I DON'T want to talk about today is DNS poisoning or spoofing, or similar vulnerabilities. While cool, it generally requires the attacker to take advantage of poorly configured or vulnerable DNS servers.

Technically, I'm also releasing a tool I wrote a couple weeks ago: dnslogger.rb that replaces an old tool I wrote a million years ago.



Recursive? Authoritative? Wut?

As always, I'll start with some introduction to how DNS works. If you already know DNS, you can go ahead and skip to the next section.

DNS is recursive. That means that if you ask a server about a domain it doesn't know about (that is, a domain that isn't cached or a domain that the server isn't the authority for), it'll either pass it upstream to another DNS server (recursive) or tell you where to go for the answer (non-recursive). As always, we'll focus on recursive DNS servers - they're the fun ones!

If no interim DNS server has the entry cached, the request will eventually make it all the way to the authoritative server for the domain. For example, the authoritative server for *.skullseclabs.org is 206.220.196.59 - my server (and hopefully the server you're reading this on :) ). That is, any request that ends with skullseclabs.org - and that isn't cached - will eventually go to my server. See the next section for information on how to set up your own authoritative DNS server.

Let's look at a typical setup. You're on your home network. Your router's ip address is probably the usual 192.168.1.1, and is plugged into a cable modem. When you connect your laptop to your network, DHCP (aka, magic) happens, and your DNS server probably gets set to 192.168.1.1 (unless you've manually configured it to 8.8.8.8, which you should). When your router connects to your cable modem, more DHCP (aka, more magic) happens, and its DNS server set to the ISP's DNS server.

When you do a lookup, like "dig hello.skullseclabs.org", your computer sends a DNS request to 192.168.1.1 saying "who is hello.skullseclabs.org"? Obviously, your router has no idea - he's just a stupid Linksys or whatever - so he has to forward the request to the ISP's DNS server.

The ISP's DNS server gets the request, and it has no idea what to do with it either. It certainly doesn't know who "hello.skullseclabs.org" is, so it's gonna forward the request to its DNS server, whatever that happens to be. Or it might tell the router where to look for a non-recursive query. Since at this point it's out of our hands, it doesn't really matter.

Eventually, some DNS server along the way is going to say "hey, why don't we just go to the source?", and through a process that leading scientists believe is magic (there's a lot of magic in DNS :) ), it will look up the authoritative server for skullseclabs.org, discover it's 206.220.196.59, and send the request there.

My server will see the request, and, assuming something is listening on UDP port 53, have the opportunity to respond.

The response can be any IP address for an A (IP) or AAAA (IPv6) request; a name for a CNAME (alias) or MX (mail) request; or any ol' text for a TXT request. It can also be NXDomain - "domain not found" - or various error messages (like "servfail").

One of the cool things is that even if we return "domain not found", we still see that a request happened, even if the person doing the lookup sees that it failed! We'll see some examples of why that's cool shortly.

How do I get an authoritative server?

The sad part is, getting an authoritative server isn't free. You have to buy a domain, which is on the order of $10 / year, give or take.

Beyond that, it's just a configuration thing. I don't want to spend a ton of time talking about it here, so check out this guide, written by Irvin Zhan for instructions to do it on Namecheap.

I personally did it on Godaddy. It took some time to figure out, though, so prepare for a headache! But trust me: it's worth it.

The set up

We'll use skullseclabs.org - my test domain - for the remainder of this. Obviously, if you want to do this yourself, you'll need to replace that with whatever domain you registered. We'll also use dnslogger.rb, which you'll get if you clone dnscat2's repository.

Getting dnslogger.rb to work is mostly easy, but permissions can be a problem. To listen on UDP/53, it has to run as root. It also needs the "rubydns" gem installed in a place where it can be found. That can be a little annoying, so I apologize if it's a pain. "rvmsudo" may help.

If anybody out there is familiar with how to properly package Ruby programs, I'd love to chat! I'm making this up as I go along :)

What does DNS look like?

All right, let's mess around!

I'll start by having no DNS server running at all on skullseclabs.org - basically, the base state. From another host, if you try to ping it, you'll see this:

$ ping noserver.skullseclabs.org Ping request could not find host noserver.skullseclabs.org. Please check the name and try again.

Conclusion? It's down. If you were investigating an incident and you saw that message, you'd conclude that there's nothing there, right? Probably?

Let's fire up dnslogger.rb:

$ sudo ruby ./dnslogger.rb dnslogger v1.0.0 is starting! Starting dnslogger DNS server on 0.0.0.0:53

Then do the same ping (with a different domain, because caching can screw you up):

$ ping yesserver.skullseclabs.org Ping request could not find host yesserver.skullseclabs.org. Please check the name and try again.

It's the exact. Same. Response. The only difference is, on the DNS server, we see this:

$ sudo ruby ./dnslogger.rb dnslogger v1.0.0 is starting! Starting dnslogger DNS server on 0.0.0.0:53 Got a request for yesserver.skullseclabs.org [type = A], responding with NXDomain

What's this? We saw the request! Even if the person doing the lookup thought it failed, it didn't: WE KNOW.

That's really cool, because it's a really, really stealthy way to find out if somebody is looking you up. If you do a reverse DNS lookup for 206.220.196.59, you'll see:

$ dig -x 206.220.196.59 [...] ;; ANSWER SECTION: 59.196.220.206.in-addr.arpa. 3567 IN PTR test.skullseclabs.org.

And if you look up the forward record: