So I’m trying to setup a really tight server and one of the things left to secure was DNS. How do I make sure that if the server gets rooted the backdoor will not be able to connect through DNS to it’s C&C? I decided to write a custom “firewall dns”, which would only allow DNS requests if they matched a certain host. You might now be yelling things like “YOU RETARD, never code something if there is an existing and probably working alternative”, true; thing is I’ve never really done anything with DNS on a coding level so it seemed like this was my opportunity.
Let’s spoil the fun, here is the boring output from my “fw-dns” like I like to call it:
Starting fw-dns
Listening on localhost 127.0.0.1
Connected to remote DNS server (‘192.168.5.1’, 53)
matches, forward
Drop packet
For now it’s in the alpha stage but it seems to work exactly like I want(or at least with the tests I’ve run). So if you try to resolve a domain which is allowed, it will forward the query and relay the response. If you try a disallowed domain it will do nothing. The best thing about writing this, was the dpkt library which made writing it a breeze(agreed that I have yet to sort out a lot of DNS stuff before it’s actually reliable). For example parsing the dns packet is as easy as modifying the original example src a little bit, you literally put your brain in ‘no-brainer’ mode and it will just work:
Just receive the request:
data, addr = udps.recvfrom(4096)
and parse it to retrieve the domain name:
dns = dpkt.dns.DNS(data)
if dns.qr != dpkt.dns.DNS_Q:
return None
if dns.opcode != dpkt.dns.DNS_QUERY:
return None
if len(dns.qd) != 1:
return None
if len(dns.an) != 0:
return None
if len(dns.ns) != 0:
return None
if dns.qd[0].cls != dpkt.dns.DNS_IN:
return None
if dns.qd[0].type != dpkt.dns.DNS_A:
return Nonereturn dns.qd[0].name
the relaying is just a simple send and receive:
def relay(data):
global fudps
fudps.sendto(data,DNS_SERVER)#send original approved request
d, a = fudps.recvfrom(4096)#receive answer
return d #only return data section
Don’t you just love it when things are this easy?
You can download the software using bittorrent here.
The following resources where used for this quick hack:
http://jon.oberheide.org/blog/2008/12/20/dpkt-tutorial-3-dns-spoofing/
http://jon.oberheide.org/blog/2008/08/25/dpkt-tutorial-1-icmp-echo/
http://code.activestate.com/recipes/491264-mini-fake-dns-server/
http://docs.python.org/library/socket.html
This was meant more to prevent DNS backdoors to actually access the outside world. Only changes for my personal use have been the adding of a configuration file and the possibility to reload the config file without restarting fw-dns, also dropping privileges after starting.
note:
if you plan to use this kind of setup for a large throughput environment I suggest to either optimize the code or use powerdns with a ‘preresolve’ lua script hook.
nice work man, keep us up to date on its progress, and what other things did you do to secure the DNS server?