Sinkhole DNS
I wanted to track DNS queries that get send to nameservers that do not serve a particular domain or network. I used a Bind DNS server that logged the query and returned a fixed response. The logs get parsed by Logstash and stored in Elasticsearch for analysis.
Install bind
Installing bind is easy via the bind9 package :
sudo apt-get install bind9
This will add a new user ‘bind’ and store the configuration files in /etc/bind.
For this setup I want bind to behave as an authoritative nameserver for every possible domain and always reply with the same result.
The core bind configuration file is /etc/bind/named.conf. I commented the default zones and added a custom ‘catch-all’ DNS zone.
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
#include "/etc/bind/named.conf.default-zones";
zone "." {
type master;
//type hint;
file "/etc/bind/db.root.honeypot";
};
The zone file, /etc/bind/db.root.honeypot, has the minimal configuration to reply with 127.0.0.1 (change this to another IP if you want to track what happens after the DNS query).
$TTL 10
@ IN SOA localhost. root.localhost. (
1 ; Serial
10 ; Refresh
10 ; Retry
10 ; Expire
10 ) ; Negative Cache TTL
;
IN NS localhost
* IN A 127.0.0.1
You also have to configure some of the options of bind in /etc/bind/named.conf.options.
options {
directory "/var/cache/bind";
// forwarders {
// 8.8.8.8;
// };
dnssec-validation auto;
recursion no;
allow-transfer { none; };
auth-nxdomain no; # conform to RFC1035
// listen-on-v6 { any; };
statistics-file "/var/log/named/named_stats.txt";
memstatistics-file "/var/log/named/named_mem_stats.txt";
version "9.9.1-P2";
};
logging{
channel query_log {
file "/var/log/named/query.log";
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
category queries {
query_log;
};
};
The options above disable recursion, return a custom version number and enable logging.
- recursion no : disable recursive lookups;
- allow-transfer { none; } : no zone transfers allowed;
- statistics-file and memstatistics-file : DNS stats (via rndc);
- version “9.9.1-P2” : return a custom server version;
- // listen-on-v6 { any; }; : do not listen on IPv6;
If the logging directory, /var/log/named, doesn’t exist already then you have to create it and make sure it is owned by the user bind.
mkdir /var/log/named sudo chown bind /var/log/named
Then restart bind, check the output of your syslog messages and try some lookups.
sudo /etc/init.d/bind9 restart host www.google.com 127.0.0.1
Apparmor.d and bind
It’s possible that you get a permission denied on the log directory when restarting bind on Ubuntu.
named[11625]: isc_stdio_open '/var/log/named/query.log' failed: permission denied
This is caused by AppArmor. You can allow write access to these files by changing the AppArmor profile /etc/apparmor.d/usr.sbin.named and check that it contains
/var/log/named/** rw, /var/log/named/ rw,
Logstash configuration for Bind
Now that bind is logging properly to a text file we can configure Logstash to parse the Bind log files. The Logstash configuration file is the one that I previously used for Using ELK as a dashboard for honeypots. I only list the relevant changes below. You can get all of the configuration from Github.
############################################################
# DNS honeypot
#
if [type] == "dnshpot" {
grok {
match => [ "message", "%{MONTHDAY:day}-%{MONTH:month}-%{YEAR:year} %{TIME:time} queries: info: client %{IP:srcip}#%{DATA:srcport}%{SPACE}\(%{DATA:hostname}\): query: %{DATA:hostname2} %{DATA:querytpe3} %{DATA:querytype} %{DATA:querytype2} \(%{IP:dstip}\)" ]
}
mutate {
add_field => [ "dstport", "53" ]
}
mutate {
strip => [ "srcip", "dstip", "hostname", "srcport" , "hostname2", "querytype", "querytype2" ]
}
mutate {
add_field => [ "timestamp", "%{day}-%{month}-%{year} %{time}" ]
}
date {
match => [ "timestamp", "dd-MMM-YYYY HH:mm:ss.SSS" ]
}
}
Logrotating
Do not forget to rotate the query log file.
/var/log/named/query.log {
monthly
rotate 12
compress
delaycompress
missingok
notifempty
create 644 root root
}


Hi
This was exactly what I was looking for. I only have 1 problem, when a Windows host does a lookup, it appends my local domain to the lookup. How do I prevent this or can I forward these requests to our actual name servers? Complete noob when it comes to bind.
Thanks for the fantastic write-up.