Foiling DNS Attacks

By Jay Beale ( jay@bastille-linux.org ), Lead Developer, Bastille Linux Project, Principal Consultant JJB Security Consulting and Training (C) 2000, Jay Beale (C) 2000, Jay Beale

Most of us take DNS servers for granted. Here, in a continuing series on attacking and defending your own machines, I discuss how people attack DNS servers and what you can do to better your security. Slightly more specifically, I'll show you:



I'll discuss this in Attack/Defense manner, the same way I did in Anyone With a Screwdriver Can Break In! The "defense" will be implemented on a BIND 8 DNS server, but the concepts apply to all DNS servers. This article should be useful to managers and admins alike, though the former will find the attacks and general concepts more interesting than the technical specifics of defense. So, here we go...

Attack: Getting too much information from your DNS Servers

OK, so we're trying to profile the domain dumb.target.jay. Let's fire up nslookup, the primary DNS query tool. First, we need to know what dumb.target.jay's nameservers are. Let's query on that:


[jay@max jay]$ nslookup
Default Server: ns.my.isp
Address: 10.0.0.1

> set q=ns
> dumb.target.jay
Server: ns.my.isp
Address: 10.0.0.1

Non-authoritative answer:
dumb.target.jay nameserver = dumb.target.jay
dumb.target.jay nameserver = ns2.dumb.target.jay

Authoritative answers can be found from:
dumb.target.jay internet address = 192.168.1.85
>


OK, so now we know what server to query. Let's ask this server for a complete listing of the zone, using dig to get a zone transfer.


[jay@max zone]# dig @192.168.1.85 dumb.target.jay axfr

; <<>> DiG 8.2 <<>> @192.168.1.85 dumb.target.jay axfr
; (1 server found)
$ORIGIN dumb.target.jay.
@                       20H IN SOA      ns1 hostmaster.dumb.target.jay (
                                        2000111001      ; serial
                                        5H              ; refresh
                                        1H              ; retry
                                        4d4h            ; expire
                                        1D )            ; minimum

                        1H IN NS        dumb.target.jay.
                        20H IN NS       ns.dumb.target.jay.
                        20H IN NS       ns.dumbs.isp.
                        20H IN A        192.168.1.85
                        1D IN HINFO     "Pentium 133" "Red Hat 6.1"
                        1D IN MX        10 mail
mail                    1D IN A         192.168.1.2
really                  1D IN A         192.168.1.20
                        1D IN TXT       "Admin's Trusted Workstation"
                        1D IN HINFO     "Athlon 850" "Red Hat 6.1"
rather                  1D IN A         192.168.1.15
                        1D IN HINFO     "Pentium 266" "Mandrake 7.1"
serious                 1D IN CNAME     extra
extra                   1D IN A         192.168.1.80
ns                      1D IN A         192.168.1.30
r_g                     1D IN A         192.168.1.68
roblimo                 1D IN MX        10 r_g
                        1D IN A         192.168.1.44
tahara                  1D IN A         192.168.1.27


Look at all the information we grabbed here. Two commands and now we've got a list of all the machines in this zone! Further, if you look at the HINFO records, you can learn what Linux distribution these machines are running. What does that give us? Well, I've got a list of exploits that work against Red Hat 6.1. If I do a zone transfer, as we did above, and then search for "Red Hat," I find two vulnerable machines, including the sysadmin's workstation. From there, I can focus my attack on these two machines. I'll put extra effort into cracking the admin's workstation, which usually has trust relationships with other valuable machines. These two DNS queries have been very useful to us - in fact, they're among the first steps a cracker makes when profiling a network!

Defense: Configuring DNS Servers Intelligently

OK, so DNS was designed when the Internet was a much more trusting place. Too many large sites still configure their DNS servers to give out tons of extra information to anyone who asks. These HINFO and TXT records, while useful to internal site administrators trying to maintain tons of machines, are far more useful to external crackers trying to profile your company's computers.


DNS records only have to give IP/name mappings. You sure wouldn't make this mistake with your physical environment, would you? Would you let the receptionist give away not only people's extensions, but also their positions, qualifications, and project plan? Of course not! You'd risk corporate info and probably lose people when the head hunters got wise!


We can easily configure your nameserver to only give information to those who need it. Let's edit the /etc/named.conf file, the master configuration file for BIND.


options {
        directory "/var/named";
};

zone "dumb.target.jay" {
        type master;
        file "zone/db.dumb.target.jay";
};

zone "1.168.192.in-addr.arpa" {
        type master;
        file "zone/db.192.168.1";
};


OK, so this configuration just lists an options block, which shows global settings, and the forward and reverse zones for a fictional dumb.target.jay domain. We've left out a rootservers entry and a localhost entry, for brevity. This example is for a primary/master name server, rather than a secondary/slave.


First, let's consider zone transfers. Zone transfers are normally used only to keep secondary/slave nameservers up to date with the primary/master nameservers. Other than this, they're really only used to profile a target. Well, suppose your only secondary nameservers have IP addresses 192.168.1.30 and 10.1.1.4. Then no other machines should be making zone transfers, right? Let's restrict this, like so:


options {
        directory "/var/named";
        allow-transfer { 192.168.1.30; 10.1.1.4; };
};

Suppose your internal network consists only of machines on the 192.168.1.x subnet. Machines outside this subnet should only query you for zones that you administer. This is important because most attacks on a DNS server require that an attacker can query said server, usually for a domain that the attacker controls. If we don't let an outsider query us for a domain that we don't own, we've foil many attacks.


options {
        directory "/var/named";
        allow-transfer { 192.168.1.30; 10.1.1.4; };
        allow-query { 192.168.1.0/24; };
};

zone "dumb.target.jay" {
        type master;
        file "zone/db.dumb.target.jay";
        allow-query { any; };
};

zone "1.168.192.in-addr.arpa" {
        type master;
        file "zone/db.192.168.1";
        allow-query { any; };
};


OK, so everyone can query us for the domain dumb.target.jay and its reverse, but only internal hosts can make other queries. Finally, let's allow recursive queries only from internal hosts. This has to do with the way that individual hosts, equipped with only a resolver library, ask nameservers for name/IP mappings. An individual host will ask for dumb.target.jay's IP. The nameserver first queries a rootserver to find someone responsible for the .jay domain. The rootserver gives it some IP, which it then contacts and asks for someone responsible for the .target.jay domain. It then queries this nameserver, asking for dumb.target.jay. Only your internal hosts should be able to ask your nameserver to do this. External hosts will have their own nameservers. Allowing those hosts ask your nameserver to answer recursive queries can open you up to certain kinds of cache poisoning attacks and generally just gives too much access to the outsider.


options {
        directory "/var/named";
        allow-transfer { 192.168.1.30; 10.1.1.4; };
        allow-query { 192.168.1.0/24; };
        allow-recursion { 192.168.1.0/24; };
};


OK, so we've really, really tightened up our nameserver. What's left? Well, someone can still query those sensitive HINFO and TXT records, even if they can't do zone transfers. Like this:


[jay@max jay]$ dig @localhost dumb.target.jay hinfo

; <<>> DiG 8.2 <<>> @localhost dumb.target.jay hinfo 
; (1 server found)
;; res options: init recurs defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUERY SECTION:
;;      dumb.target.jay, type = HINFO, class = IN

;; ANSWER SECTION:
dumb.target.jay.        1D IN HINFO     "Pentium 166" "Red Hat 6.1"

;; Total query time: 1 msec
;; FROM: max.fictional.attacker to SERVER: localhost  127.0.0.1
;; WHEN: Sun Nov 12 00:17:08 2000
;; MSG SIZE  sent: 33  rcvd: 69

There's really no magic defense here. You have two choices:



The latter simply means making two sets of nameservers, one inside your firewall and the other outside. I'll share the basics here, but going into the details would take an additional article or two. (PLUG: There's a section on this in my as-yet-unfinished book!) Here's the basics: you use Network Address Translation (NAT) on your internal network and use an internal set of nameservers, as well as an external set. The internal set gets all the information, including TXT and HINFO, while the external set has a very spartan name database. The external ones often don't list anything about most of your internal clients, since the NAT makes them all look like they're a single machine anyway.


Split Horizon DNS is a great deal of work, and most of you will save this for the next major DNS project. For now, please be very careful about those HINFO/TXT records, and your DNS records in general. Remember too follow the Principle of Minimalism and only give outsiders what information they must have. Don't name your sysadmin's machine admin and maybe rethink calling it overlord!

Attack: Cracking Your Name Server

OK, so we've really cleaned up on data-mining attacks, right? Well, the BIND DNS server itself has exploitable security vulnerabilities from time to time. For instance, BIND 8.2 - 8.2.2, non-patched, is vulnerable to a remote-root exploit! You can read about it here.


Many, many script kiddies were scanning large blocks of the Internet for vulnerable versions, like this:


[jay@max bog]$ dig @dumb.target.jay dumb.target.jay txt chaos version.bind

; <<>> DiG 8.2 <<>> @dumb.target.jay dumb.target.jay txt chaos version.bind 
; (1 server found)
;; res options: init recurs defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUERY SECTION:
;; version.bind, type = TXT, class = CHAOS

;; ANSWER SECTION:
VERSION.BIND. 0S CHAOS TXT "8.2.2"

;; Total query time: 1 msec
;; FROM: max.fictional.attacker to SERVER: dumb.target.jay 192.168.1.85
;; WHEN: Mon Oct 20 18:30:05 2000
;; MSG SIZE sent: 30 rcvd: 63

[jay@max bog]$ 


Hey! We've got the version number! And it's vulnerable. Why is this so heinously bad? Because, as I point out in "Why Do I Have to Tighten Security?", script kiddies tend to scan the Internet blindly looking for vulnerable servers. They might download (or, gods, write!) a three line perl script to query a large block of addresses looking for vulnerable nameservers. They could find ours! Further, if a knowledgeable crackers is profiling us, he'll definitely want to know whether we've got a vulnerable nameserver. He'd love to get that info without making a noisy attack!

Defense: Obscure Your BIND Version From Crackers and Their Scripts, Patch, Run as Non-root User

Luckily, we can change what string our nameserver comes back with. Make the following addition to your /etc/named.conf options block:


options {
        directory "/var/named";
        allow-transfer { 192.168.1.30; 10.1.1.4; };
        allow-query { 192.168.1.0/24; };
        version "Go away!";
};

You might instead use the string "10.0.0" to be less offensive, or "4.9.7" to throw them off the track.


On top of this measure, you really should make sure to keep your nameserver up to date. Patch that sucker religiously! Even doing this, though, you'll still have small windows of vulnerability. With most DNS server programs running as root, this tends to open you to remote root exploits, whenever they're discovered, until they're patched. During this time, the only thing that will save you is hardening the nameserver. First, and fairly easily, set BIND to run as an alternate user, if it's not doing so. You can find out what user BIND runs as by typing the following:


[jay@max jay]$ ps -ef | grep named
named     9179     1  0 12:15 ?        00:00:00 named -u named 


That first column shows the user. On my test box, Mandrake 7.1, BIND's named runs as a lowly user called named. If you're running an operating system that shows named running as root, find the init script that activates named and modify it to use the -u and -g flags, like so:


/usr/sbin/named -u dns_user -g dns_group


You'll have to create a user called dns_user and a group called dns_group. You can skip these boring names, and use whatever you like. MandrakeSoft picked named for the user and root for the group. I might pick bob and less, just to throw curious users/crackers off. In any case, please use both flags. By the way, whatever user that you create should have a disabled shell, like /bin/false and should have a home directory of maybe /var/named, or wherever your DNS directory is.


This simple step would have removed the "root" from the "remote root exploit" in BIND 8.2-8.2.2 nameservers. It would only have granted the script kiddie a normal user shell, which they would have had to escalate privilege to get a root shell, if they could. For most script kiddies, it would have stopped the attack cold. Now, if we want to stop the advanced cracker's attack cold, we'll have to take one more step.

Advanced Defense: Chroot the Server

We can confine the server to run in a "chroot prison." This basically confines the server to only accessing files in a subset of the filesystem, often something like /home/dns or /chroot/dns. Bastille Linux's DNS.pm module will chroot the DNS server from a Red Hat 6.0-6.2 system. I've included scriptish instructions on my web site that will do the job on a bare Red Hat 6.0 system. In any case, expect a future article/book section on this defense. If you're really eager, there are a couple good articles on chroot-ing out there. It's a pain, but well worth it!

Wrap up

OK, so what have we done? We've demonstrated the DNS portion of target profiling and implemented the defenses. We've blocked many cache poisoning and server attacks by defining exactly who can query our servers. We've blocked many data mining attacks by restricting zone transfers. We've foiled version-grabbing scripts by not giving away an accurate BIND version number. Finally, we tried to restrict the possible damage of a direct attack on the nameserver by running it as an alternate user. Not a bad day, huh? For those of you who're itching for more, look immediately to Split Horizon DNS and Chrooting your Nameserver.


Jay Beale is the Lead Developer of the Bastille Linux Project (http://www.bastille-linux.org). He is the author of a number of articles on Unix/Linux security, along with the upcoming book "Securing Linux the Bastille Way," to be published by Addison Wesley. (By the way, the book discusses all this, along with the advanced topics, in greater depth.) At his previous day job, Jay was a security admin working on Solaris and Linux boxes. At his current job, Jay is working on a well-known Linux distribution. You can learn more about his articles, talks and favorite security links via http://www.bastille-linux.org/jay.