Protecting Users with DNS Malware Blacklisting

Blacklisting domains that are known to host malware is a simple way to discovery and stall malware on your organisations network. You can use it to prevent users, or at least warn them severely, from installing it in the first place and discover malware making dubious DNS lookups on your network too. As a good network administrator is one that maximises laziness, we want to avoid the issues of false positives; mainly as then this increases the amount of contact with have with the users and that is just asking for trouble. The instructions here not only cover how to blacklist domains, but also to keep your list automatically up to date, and add the functionality so that users themselves can bypass what they consider to be a false positive listing.

Blacklists where malware lives is hard to come by, as searching online really only gives you hits for DNS mail based blacklisting to be used on MTAs. The only ones I have found are:

For now I have focused on malwaredomain's list, since we (4000 students and 600 staff) started using this system back in July 2008-ish I have had no problems, and more importantly no contact with the users in regards to it.

If you have any problems, queries, or suggestions for improvement then do please contact me.

Requirements

You need to have a already functioning installation of Unbound running as your organisation's recursive DNS server. You might prefer to use BIND9, MaraDNS or some other resolver however to do so you will need to adapt my shell script and instructions accordingly, although the Apache bit should remain the same. How to do this all is beyond the scope of this article.

In addition to this you will need a seperate server with a the Apache webserver installed on it that is able to make recursive DNS queries all by it's self. In addition, you will need the following installed and configured on the box:

Configuring the Infrastructure

The Update Script

The script is intelligent enough to check the 'last-modified' timestamp in the HTTP header for the 'domains.txt' file and to only download and process it when it changes. This considerably lowers the load for the 'Malware Domains' owner and helps to avoid un-necessary restarts of Unbound.

Download the malwaredomains2unbound script, make it executable and place it in '/usr/local/sbin/'. You will need to make a few minor amendments at the top to match your setup:

TYPE=redirect # or 'redirect' or 'refuse, consult 'man unbound.conf'
DST_HOST=ids.example.com.
#DST_MAIL=localhost.

These should be the only lines you need to amend. If you simply want to prevent the DNS lookups working (and not to have a 'self-help' whitelisting service as detailed below functioning) then set 'TYPE' to 'refuse'. Otherwise you will need to enter in a FQDN (it is crucial that you terminate it with a '.', unless you know what you are doing) to say where you want the DNS lookups to go instead. You also get the option to redirect the MX records (if you do not, then none will exist and you will get NXDOMAIN) by populating 'DST_MAIL' with a FQDN.

Once configured, you should be able to give it a test run by typing as root (this will take some time so be patient):

# malwaredomains2unbound

Once it runs you should see that a 'domains.txt' file exists in '/var/tmp/' and an updated 'local-dnshijack' file is present lurking in '/etc/unbound/'. If you re-run the script again, it should do nothing as the timestamp of the remote 'domain.txt' file should match your local one (ie. it's unchanged).

The DNS Componment

To configure Unbound is dead easy, you just add the line at the end of the main "server:" section (before the 'python' and 'remote' sections start) to say:

include: "/etc/unbound/local-dnshijack"

If you now restart unbound you should find it now works.

Scheduling the Updates

If you create a file '/etc/cron.d/local-unbound' with the following contents:

#MAILTO=hostmaster
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# I hope you use DNSSEC....right?
17 2 7 * *      root    /usr/local/sbin/update-itar.sh && unbound-checkconf && /etc/init.d/unbound restart

# Every two hours check for updates
# N.B. for each DNS server you should put a different minutely setting of about five minutes
#      as when unbound restarts you will find for about ten seconds you will not get any DNS
#      lookups functioning.  Spreading the updates means only one DNS server is down at a time
15 0-23/2 * * * root    /usr/local/sbin/malwaredomains2unbound && unbound-checkconf && /etc/init.d/unbound restart

This will check for updates every two hours at fifteen minutes past the hour. As the note in the above chunk states, you should choose different minutely intervals for each of your DNS servers to avoid downtime; as obviously Unbound will not service DNS queries whilst being restarted which can take some time.

If the 'domains.txt' file has not been updated or there was a problem running the 'malwaredomains2unbound' script, or the config file becomes broken, then no restart of Unbound will take place. You should, if you have configured cron correctly, receive an email stating what went horribly wrong if the script failed to run.

The Webserver Componment

The element that makes this bit cook are:

To get the dnshijack'ing code working you will need to only edit the Apache VirtualHost file, '/etc/apache2/sites-available/dnshijack'. You need to:

You will also need to tweak the section listing details about your installation in that file too:

PerlSetEnv dnshijackNS                "1.1.1.1"
PerlSetEnv dnshijackREALM             "example.com"
PerlSetEnv dnshijackCONTACT_NAME      "Firstname Surname"
ServerAdmin                           "me@example.com"
PerlSetEnv dnshijackKEY               "TYPE SOME RANDOM SECRET HERE SO MASH KEYBOARD OR SOMETHING"
PerlSetEnv dnshijackDEFAULT_LANGUAGE  "en"

You need to amend it as follows:

You need to configure your webserver to run it's own recursive DNS server that functions completely independently from your organisations one so you should tweak your '/etc/resolv.conf' file to use the nameserver at '127.0.0.1'. Without this, your webserver when proxying requests would simply find it's-self talking back to it's-self in a nasty loop when someone tries to go to a blacklisted domain. This you obviously do not want to happen.

All you need to do is restart Apache and you should have a fully functional DNS blacklisting system active. When you try to access a blacklisted domain with your web browser you will be redirected to a 'blacklisted' page giving a simple explaination of why the site has been disabled. If it is considered a false positive then a simple button "accept responsibility" will enable the user to get through to the site they want.

How It Works

It should be pretty obvious why the user sees the disabled page 1, the whitelisting system might need some explaination.

When a user wishes to access a system, a client side session cookie is set that enables them to pass through the Apache server. The cookie is presented to the blacklist script, 'DNShijack.pm', and if it checks out to be valid the requst is passed to mod_proxy and handled approiately, the cookie is stripped out on it's way though so the destination webserver never sees it. This is where the server side KEY is considered, so malicious users cannot amend the cookie to whitelist alternative domains too. Now, when that cookie is set Apache will not log requests made to it that are blacklisted so privacy concerns should also be a non-issue. The only things in the log you should see is the initial request where a user hits the page, and an entry in the error.log stating that the user has accepted the 'risk' in going to the site.

N.B. bear in mind that this system is ineffective against anything that directly accesses the IP address of a blacklisted domain, no DNS lookup, no blacklisting. This sort of thing probably should be handled by your firewall. So if the hosts file is tweaked on the workstation it's-self, then the user will not benefit from this type of system. Alternatively you might want to look into my Unsavoury IP Route Blackholing page to solve this.

Interesting Projects

To customise the webpages the user sees all you need to do is amend '/var/local/dnshijack/templates/en/' (copy the directory and name it to the i18n language you use if you want to regionise the system) and edit the file to your hearts content; no need to restart Apache after changing this.

If you packet sniff on the network interface of the webserver and filter ports 53 and 80:

# tcpdump -i bond0 -n -p host ids.example.com and not port 53 and not port 80

You will be able to catch all the 'strange' requests being made to the blacklisted domain, probably finding malware your AV is unable to.

  1. as instead of the real IP address being returned for a blacklisted the webserver one is returned (1)

www: dns-malware-blacklisting (last edited 2010-01-08 15:32:37 by alex)