Sinkholing malware domains using Bind DNS

Bind is a good DNS server, however it has a slightly chequered history insofar as vulnerabilities are concerned. Nevertheless it’s a good solution for internal networks, and offers some flexible configuration options. In this post I’m going to add some security filtering functionality using Perl, Bash and some standard Linux tools.

In my earlier blog post I discussed how to block malicious domains using a Squid proxy. This is a good solution in some cases, but we can’t guarantee all endpoints will use the Squid proxy for web requests, and in those cases some requests to malicious domains may occur. Some mobile devices require extra configuration, and guest users of your network may not configure their devices to use the proxy at all. Some device types may not support the use of a proxy.

If we can supply the domains to the Bind DNS server, we can also cut off any access attempts at DNS resolution time. This benefits a network in two ways: firstly, we can block undesirable DNS resolution requests from client resolvers; secondly, we can block undesirable DNS resolution requests from a Squid proxy adding a backstop.

I’ve more or less implemented the JISC guide on how to do this (https://community.jisc.ac.uk/library/janet-services-documentation/how-block-or-sinkhole-domains-bind), and steps below mix it with my malicious domain filtering system described in my previous blog post.

I’ve had to make some edits for my implementation and correct a nameserver error to get results however (details below).

The basic idea is to make your DNS server authoritative for the domains you wish to block. Instead of reaching out to the Internet server for the domain, your server acts as the authority and redirects the requestor to a benign IP address. In this case I want to redirect to 127.0.0.1 for IPv4 or ::1 for IPv6. You can, as JISC suggest redirect to another IP address that you could use to generate MI.

The first step is to include the following line in /etc/bind/named.conf.local:

include "/etc/bind/blacklisted.zones";

Then we need to create zones for the malicious domains. Fortunately one of the files created in my previous (messy) shell script outputs the SLDs, which we’ll now process using some Perl:

#!/usr/bin/perl -w
use strict;
my $filename = "db_new2.txt";
open (my $fh, $filename)
   or die "Could not open file";
while (my $row = <$fh>) {
   chomp $row;
   printf "zone \"${row}\" { type master; file \"/etc/bind/zones/blockeddomains.db\";};\n";
}

The script above creates a file containing lines as follows:

zone "blockeddomain.com" {type master​; file "/etc/bind/zones/blockeddomains.db";};

Finally we define a zone spec in the file referred to above:

$TTL    3600
@       IN      SOA     ns1.example.local. info.example.local. (
                            2014052101         ; Serial
                                  7200         ; Refresh
                                   120         ; Retry
                               2419200         ; Expire
                                  3600)        ; Default TTL
;
        IN      NS      127.0.0.1
                A       127.0.0.1 ; This means that SLD.com gets directed to the designated address
*       IN      A       127.0.0.1 ; This wildcard entry means that any permutation of xxx.SLD.com gets directed to the designated address
                AAAA    ::1 ; This means that SLD.com gets directed to IPv6 localhost
*       IN      AAAA    ::1 ; This wildcard entry means that any permutation of xxx.SLD.com gets directed to IPv6 localhost

I’ve added a nameserver line to the above zone spec to avoid load errors. The above steps implement the required functionality, and now make Bind responsive to our dynamically updated malicious domains list and give it the ability to deny requests for known malicious domains.

Now to try a test:

# nslookup www.zndxa.com
Server:         127.0.0.1
Address:        127.0.0.1#53
Name:   www.zndxa.com
Address: 127.0.0.1

As we can see, it works well. The domain listed is given a benign response, and the client is protected. And that’s it. Point the clients to the DNS server and resolution attempts for the server should fail for all permutations of the SLD.

What other steps can be taken? As I mentioned in my previous blog post, the best architectural pattern is to deploy a border/edge firewall that only accepts DNS/proxy traffic from your internal servers.

Auto-configuring proxy servers for clients can be accomplished using WPAD and DNS entries in Bind, however you’ll need endpoints to be talking to your DNS server in the first place to take advantage of that.

Populating the DNS server IP address in endpoint network configurations is also a manual activity, which runs against the issue we’re attempting to solve here (non-configured endpoints). The solution to that is to implement a DHCP server on the LAN, e.g. using isc-dhcp-server.