I was reading on DNS rebinding and how browsers protect us with DNS pinning.
I was curious how public DNS servers reply when you do a query for a host that is binded to an RFC1918 address.
DNS rebinding basically works as follows.
- Attackers control the DNS of a domain (‘www.example.com’);
- A user is lured (phishing, web commment, …) into visiting a site controlled by the attacker, the DNS response is a public IP but with a very short lifetime (TTL);
- When visiting the site, a piece of malicious client-side code is downloaded to the user machine;
- This code runs in the context of the browser and this code can access www.example.com
- The attacker updates the DNS entry with an RFC1918 address;
- Every request by that script (SOP applies) to www.example.com now goes to an internal address;
- As a consequence, that same script can now reach an internal host;
- A low TTL for the replies with internal addresses and some form of automatic refresh (AJAX) would allow for a basic mapping of the internal hosts.
All this http traffic is allowed because it complies with the Same Origin Policy.
One of the protection mechanisms against this type of attack is done with DNS pinning where browsers ‘lock’ the initial first DNS reply. Sometimes this can break entities that rely on dynamic DNS systems.
There are of course other techniques to attack internal hosts. This technique seems rather trivial to achieve (you can think of a scenario where the initial http request has a parameter that triggers the DNS update) and protection is not that complicated (filter out DNS replies).
I was surprised that all public DNS servers seemed to return the private IP address.
You can check this for yourself with the small script below.
It is build with a list of public DNS servers found at http://pcsupport.about.com/od/tipstricks/a/free-public-dns-servers.htm and queries the servers with and without recursion for a host that has a 192.168.1.x or 10.x.x.x address.
#!/bin/bash # Query public nameservers for RFC 1918 responses for 'test_host' # Koen Van Impe # # DNS server list form http://pcsupport.about.com/od/tipstricks/a/free-public-dns-servers.htm declare -A dns_servers dns_servers=( [google_1]=8.8.8.8 [google_2]=8.8.4.4 [level3_1]=209.244.0.3 [level3_2]=209.244.0.4 [comodo_1]=8.26.56.26 [comodo_2]=8.20.247.20 [opendns_1]=208.67.222.222 [opendns_2]=208.67.220.220 [dns_adv_1]=156.154.70.1 [dns_adv_2]=156.154.71.1 [norton_1]=199.85.126.10 [norton_2]=199.85.127.10 [greenteam_1]=81.218.119.11 [greenteam_2]=209.88.198.133 [safedns_1]=195.46.39.39 [safedns_2]=195.46.39.40 [opennic_1]=216.87.84.211 [opennic_2]=23.90.4.6 [publicroot_1]=199.5.157.131 [publicroot_2]=208.71.35.137 [smartviper_1]=208.76.50.50 [smartviper_2]=208.76.51.51 [dyn_1]=216.146.35.35 [dyn_2]=216.146.36.36 [censurf_1]=89.233.43.71 [censurf_2]=89.104.194.142 [hurricane]=74.82.42.42 [puntcat]=109.69.8.51 ) test_host=intranet.ckers.be for i in "${!dns_servers[@]}"; do srv=${dns_servers[$i]} nr=`dig @$srv +short +norec $test_host` r=`dig @$srv +short $test_host` echo "At $i no recursion : $nr" echo "At $i with recursion : $r" done
The output of the script
At publicroot_2 no recursion : At publicroot_2 with recursion : At censurf_2 no recursion : At censurf_2 with recursion : 192.168.1.1 At censurf_1 no recursion : At censurf_1 with recursion : 192.168.1.1 At publicroot_1 no recursion : ;; connection timed out; no servers could be reached At publicroot_1 with recursion : ;; connection timed out; no servers could be reached At opendns_1 no recursion : 192.168.1.1 At opendns_1 with recursion : 192.168.1.1 At opendns_2 no recursion : 192.168.1.1 At opendns_2 with recursion : 192.168.1.1 At safedns_1 no recursion : At safedns_1 with recursion : 192.168.1.1 At google_1 no recursion : At google_1 with recursion : 192.168.1.1 At google_2 no recursion : At google_2 with recursion : 192.168.1.1 At safedns_2 no recursion : At safedns_2 with recursion : 192.168.1.1 At level3_1 no recursion : At level3_1 with recursion : 192.168.1.1 At level3_2 no recursion : At level3_2 with recursion : 192.168.1.1 At hurricane no recursion : At hurricane with recursion : 192.168.1.1 At opennic_2 no recursion : At opennic_2 with recursion : 192.168.1.1 At opennic_1 no recursion : At opennic_1 with recursion : 192.168.1.1 At puntcat no recursion : At puntcat with recursion : 192.168.1.1 At smartviper_1 no recursion : At smartviper_1 with recursion : 192.168.1.1 At greenteam_2 no recursion : 192.168.1.1 At greenteam_2 with recursion : 192.168.1.1 At smartviper_2 no recursion : At smartviper_2 with recursion : 192.168.1.1 At greenteam_1 no recursion : 192.168.1.1 At greenteam_1 with recursion : 192.168.1.1 At norton_1 no recursion : ;; connection timed out; no servers could be reached At norton_1 with recursion : 192.168.1.1 At dns_adv_2 no recursion : ;; connection timed out; no servers could be reached At dns_adv_2 with recursion : 192.168.1.1 At norton_2 no recursion : ;; connection timed out; no servers could be reached At norton_2 with recursion : 192.168.1.1 At dns_adv_1 no recursion : ;; connection timed out; no servers could be reached At dns_adv_1 with recursion : 192.168.1.1 At dyn_1 no recursion : At dyn_1 with recursion : 192.168.1.1 At dyn_2 no recursion : At dyn_2 with recursion : 192.168.1.1 At comodo_1 no recursion : At comodo_1 with recursion : 192.168.1.1 At comodo_2 no recursion : At comodo_2 with recursion : 192.168.1.1
You can verify the chain of replies (in this example against one of the Google DNS servers) with
dig @8.8.4.4 +trace intranet.ckers.be
; <<>> DiG 9.8.1-P1 <<>> @8.8.4.4 +trace intranet.ckers.be ; (1 server found) ;; global options: +cmd . 4673 IN NS f.root-servers.net. . 4673 IN NS j.root-servers.net. . 4673 IN NS m.root-servers.net. . 4673 IN NS i.root-servers.net. . 4673 IN NS h.root-servers.net. . 4673 IN NS c.root-servers.net. . 4673 IN NS d.root-servers.net. . 4673 IN NS l.root-servers.net. . 4673 IN NS k.root-servers.net. . 4673 IN NS e.root-servers.net. . 4673 IN NS b.root-servers.net. . 4673 IN NS a.root-servers.net. . 4673 IN NS g.root-servers.net. ;; Received 228 bytes from 8.8.4.4#53(8.8.4.4) in 134 ms be. 172800 IN NS y.ns.dns.be. be. 172800 IN NS x.ns.dns.be. be. 172800 IN NS d.ns.dns.be. be. 172800 IN NS c.ns.dns.be. be. 172800 IN NS b.ns.dns.be. be. 172800 IN NS a.ns.dns.be. ;; Received 402 bytes from 198.41.0.4#53(198.41.0.4) in 73 ms ckers.be. 86400 IN NS b.dns.gandi.net. ckers.be. 86400 IN NS a.dns.gandi.net. ckers.be. 86400 IN NS c.dns.gandi.net. ;; Received 96 bytes from 120.29.253.8#53(120.29.253.8) in 63 ms intranet.ckers.be. 300 IN A 192.168.1.1 ckers.be. 10800 IN NS a.dns.gandi.net. ckers.be. 10800 IN NS c.dns.gandi.net. ckers.be. 10800 IN NS b.dns.gandi.net. ;; Received 112 bytes from 217.70.184.40#53(217.70.184.40) in 62 ms
Ignoring recursion
The first query is done with no recursion set, the second query is done with recursion enabled.
I expected that none of the first queries would render a reply. Surprisingly OpenDNS and GreenTeam servers replied with the correct answer, even with no recursion enabled.
I’ve tried this for two hosts (intranet.ckers.be and wpad.ckers.be) to bypass earlier caching but both requests gave the same results, regardless if recursion was enabled or not.
Update 19:30
After further investigation I found out that the output of OpenDNS was due to the fact that my test machine received a new DHCP lease. The manual setting (to point to a local DNS server) for doing queries was overridden by the default settings (the OpenDNS servers).
The servers at GreenTeam still show a reply, regardless wether recursion is set or not.
; <<>> DiG 9.8.3-P1 <<>> @209.88.198.133 +norec abc1.ckers.be ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44356 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;abc1.ckers.be. IN A ;; ANSWER SECTION: abc1.ckers.be. 299 IN A 192.168.1.10 ;; Query time: 437 msec ;; SERVER: 209.88.198.133#53(209.88.198.133) ;; WHEN: Tue Mar 18 19:08:02 2014 ;; MSG SIZE rcvd: 60 ; <<>> DiG 9.8.3-P1 <<>> @209.88.198.133 abc1.ckers.be ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33608 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;abc1.ckers.be. IN A ;; ANSWER SECTION: abc1.ckers.be. 299 IN A 192.168.1.10 ;; Query time: 166 msec ;; SERVER: 209.88.198.133#53(209.88.198.133) ;; WHEN: Tue Mar 18 19:08:02 2014 ;; MSG SIZE rcvd: 60
Update 19:45
If you use OpenDNS you can filter out RFC1918 replies. Do this in the OpenDNS Dashboard, Settings, Security.