Another great tool from Babak Farrokhi is dnstraceroute. It is part of the DNSDiag toolkit from which I already showed the dnsping feature. With dnstraceroute you can verify whether a DNS request is indeed answered by the correct DNS server destination or whether a man-in-the-middle has spoofed/hijacked the DNS reply. It works by using the traceroute trick by incrementing the TTL value within the IP header from 1 to 30.
Beside detecting malicious DNS spoofing attacks, it can also be used to verify security features such as DNS sinkholing. I am showing the usage as well as a test case for verifying a sinkhole feature.
./dnstraceroute
To use dnstraceroute, the GitHub repository must be cloned, as already shown on other sites or on the official documentation:
git clone https://github.com/farrokhi/dnsdiag.git cd dnsdiag git submodule update --init
Since dnstraceroute needs root privileges, it must be run with sudo (or the like). It basically needs the hostname to be tested against the first DNS resolver of the system. With other options the DNS server can be set manually
-s <server>or some expert information can be displayed
-x.
Here is a typical execution of dnstraceroute. I tested the domain “blog.webernetz.net” to the Google public DNS server 8.8.8.8:
weberjoh@jw-nb12-lx:~/dnsdiag$ sudo ./dnstraceroute.py -x -s 8.8.8.8 blog.webernetz.net dnstraceroute.py DNS: 8.8.8.8:53, hostname: blog.webernetz.net, rdatatype: A 1 pa-dmz.webernetz.net (192.168.110.1) 1 ms 2 80.154.108.225 (80.154.108.225) 3 ms 3 f-eb12.F.DE.NET.DTAG.DE (62.154.10.9) 3 ms 4 217.239.49.206 (217.239.49.206) 4 ms 5 72.14.196.17 (72.14.196.17) 3 ms 6 216.239.56.110 (216.239.56.110) 3 ms 7 216.239.57.188 (216.239.57.188) 4 ms 8 209.85.251.178 (209.85.251.178) 6 ms 9 209.85.255.39 (209.85.255.39) 18 ms 10 108.170.234.47 (108.170.234.47) 13 ms 11 * 12 google-public-dns-a.google.com (8.8.8.8) 12 ms === Expert Hints === [*] public DNS server is next to an invisible hop (probably a firewall)
Captured with Wireshark, it looks like this: Beginning with a TTL of 1, the value is incremented until the reply came. The ICMP time-to-live exceeded messages are displayed as the hops by dnstraceroute:
DNS Sinkholing
I am happy that my ISPs here in Germany are not spoofing my DNS queries. That is, I could not test such a scenario. (Refer to “Is Your ISP Hijacking Your DNS Traffic?” at RIPE Labs for such examples.) BUT, with dnstraceroute some “good” spoofing examples can be tested, too, such as DNS sinkholing. With this technique, next-generation firewalls such as the Palo Alto Networks firewall reply to DNS queries for malicious sites with a sinkhole IP address in order to protect the client before he even TCP-SYNed to the destination.
I tested a domain name that was listed as malicious with dnstraceroute. Obviously, the real 8.8.8.8 is not directly connected to my network ;), but listed as the first and only hop by dnstraceroute:
weberjoh@jw-nb12-lx:~/dnsdiag$ sudo ./dnstraceroute.py -x -s 8.8.8.8 3qtep.69khz.com dnstraceroute.py DNS: 8.8.8.8:53, hostname: 3qtep.69khz.com, rdatatype: A 1 google-public-dns-a.google.com (8.8.8.8) 3 ms === Expert Hints === [*] path too short (possible DNS hijacking, unless it is a local DNS resolver)
A look at the Wireshark capture shows the DNS query with a TTL with 1, but instead of an ICMP TTL exceeded reply, the Palo Alto firewall directly replied with the DNS sinkhole address:
Yeah, the DNS sinkholing works and dnstraceroute was able to verify it.
What it does not detect
Note that this tool does not detect false DNS answers from a correct DNS server. For example, though my German ISP “Deutsche Telekom” is not hijacking DNS queries, their DNS resolvers are answering with falsified A records for unregistered domains. Of course, this is a spoofed reply, but not from a man-in-the-middle but from the real DNS resolver.
In the following example I queried my home router (FRITZ!Box) which uses the DNS resolvers from Deutsche Telekom about “weberfoobar.de”, which does not exists. The DNS proxy is two hops away, so nothing seems to be wrong:
weberjoh@jw-nb12-lx:~/dnsdiag$ sudo ./dnstraceroute.py -s 192.168.7.1 weberfoobar.de dnstraceroute.py DNS: 192.168.7.1:53, hostname: weberfoobar.de, rdatatype: A 1 pa-dmz.webernetz.net (192.168.110.1) 1 ms 2 fritzbox-fdorf.webernetz.net (192.168.7.1) 24 ms
But when testing the same non-existent domain name to the Google server, the correct answer (after the correct traceroute path) is “non exist”:
weberjoh@jw-nb12-lx:~/dnsdiag$ sudo ./dnstraceroute.py -s 8.8.8.8 weberfoobar.de dnstraceroute.py DNS: 8.8.8.8:53, hostname: weberfoobar.de, rdatatype: A 1 pa-dmz.webernetz.net (192.168.110.1) 1 ms 2 80.154.108.225 (80.154.108.225) 2 ms 3 f-eb12.F.DE.NET.DTAG.DE (62.154.10.9) 10 ms 4 217.239.49.206 (217.239.49.206) 14 ms 5 72.14.196.17 (72.14.196.17) 3 ms 6 216.239.56.26 (216.239.56.26) 3 ms 7 216.239.57.125 (216.239.57.125) 22 ms 8 209.85.143.26 (209.85.143.26) 7 ms 9 209.85.255.39 (209.85.255.39) 17 ms 10 209.85.243.65 (209.85.243.65) 18 ms 11 * Invalid hostname: None of DNS query names exist: weberfoobar.de., weberfoobar.de.webernetz.net.
That is, such types of DNS spoofing must be detected with manual queries, e.g., with dig. Note that the Telekom DNS resolver answers with two A records, while the Google DNS resolver (correctly) has no answer:
weberjoh@jw-nb12-lx:~/dnsdiag$ dig @192.168.7.1 weberfoobar.de ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @192.168.7.1 weberfoobar.de ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46490 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;weberfoobar.de. IN A ;; ANSWER SECTION: weberfoobar.de. 3563 IN A 62.157.140.133 weberfoobar.de. 3563 IN A 80.156.86.78 ;; Query time: 4 msec ;; SERVER: 192.168.7.1#53(192.168.7.1) ;; WHEN: Wed Aug 31 15:24:27 CEST 2016 ;; MSG SIZE rcvd: 64 weberjoh@jw-nb12-lx:~/dnsdiag$ dig @8.8.8.8 weberfoobar.de ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @8.8.8.8 weberfoobar.de ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 53504 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;weberfoobar.de. IN A ;; AUTHORITY SECTION: de. 1799 IN SOA f.nic.de. its.denic.de. 2016083157 7200 7200 3600000 7200 ;; Query time: 31 msec ;; SERVER: 8.8.8.8#53(8.8.8.8) ;; WHEN: Wed Aug 31 15:24:36 CEST 2016 ;; MSG SIZE rcvd: 95
Cheers!
Featured image: “decisions” by Martin Fisch is licensed under CC BY-SA 2.0.