OpenDNS users may have noticed an intriguing security feature in their dashboard: the ability to block “suspicious responses.”
When enabled, this feature blocks any DNS response containing IP addresses within a private IP range:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 254.169.0.0/16
- 254.169.0.0/16
- 192.0.2.0/24
- 198.51.100.0/24
- 203.0.113.0/24
At first, that might seem a bit weird. Why would anyone want to discard legitimate responses, that are unlikely to lead to web sites worth blocking? A recently published paper has brought new attention to how these DNS responses can be exploited; let’s take this opportunity to look at what makes these responses suspicious and how we can protect against them.
Using external resources to scan a local network
When a user visits http://example.com, his web browser is going to retrieve some HTML code located at this URL, and this HTML code can contain references to additional, external resources: images, stylesheets, fonts, or javascript code. These resources may be loaded from a totally different domain name. For
example, it is common practice to load static content from a CDN.
Unless the initial page has been secured with a Content-Security-Policy header, any page can load any content from anywhere. Firefox recently blocked mixed content (loading HTTP resources from a page served over HTTPS), but that’s pretty much the only existing restriction one can come across.
This has been widely abused in the past: embedding fake images whose sources were visit trackers or targets of banner ads was a cheap and easy way to perpetrate click fraud. External resources, however, are much more interesting as an attack vector when combined with Javascript.
This simple script, for example, loads an external image (but any kind of object would do the job) at a location which is typically found in web servers running JIRA:
var node_img = document.createElement('object');
node_img.data = 'https://192.168.100.1/images/icons/issuetypes/bug.png';
node_img.style.visibility = 'hidden';
document.body.appendChild(node_img);
node_img.addEventListener('load', function() {
console.log('JIRA has been detected');
});
In this case, if the presence of JIRA on this server has been detected, a debug message is printed. But this information can also be sent to a remote server.
What is interesting here is the server address: 192.168.100.1. An attacker serving a web page with this script at http://attacker.example.com can force a web browser to access an internal server (192.168.100.1), load a resource that possibly requires authentication (through a previously set, non-ephemeral cookie), and communicate the result back to an attacker, without the user noticing anything. And of course, what works to detect a specific piece of software on a specific server can be extended to a full scan of the victim’s local network.
Taking it further using XMLHttpRequest
Javascript has the ability to issue any kind of HTTP query.
A GET query made using XMLHttpRequest is a convenient way to retrieve any kind of content, not just to test for its presence. This can be particularly interesting if this content is an internal Wiki or an internal Git repository containing proprietary source code.
XMLHttpRequest also gives access to other verbs: if an attacker can issue a POST, DELETE or UPDATE query, he can potentially alter internal databases, send emails on the behalf of the victim, and more.
For this reason, web browsers disallow cross-domain queries made that way: the host name of the initial page must match the host name of the external resource, as well as the port number and the protocol. There are mechanisms to explicitly allow cross-domain requests, but if the target is an unrelated internal server, an attacker can hardly rely on these being available.
This is where DNS comes into play.
The DNS rebinding attack
A web site served as http://attacker.example.com/* can freely send any query to http://attacker.example.com/* and retrieve the result, but this doesn’t consider the IP addresses that attacker.example.com resolves to.
Consider the following scenario:
- A victim is tricked into accessing http://attacker.example.com
- His web browser sends a DNS query to turn this name into the attacker web site IP address. The A record of the DNS response a TTL of zero.
- The victim visits the web site
- This web site performs a XMLHttpRequest query to http://attacker.example.com
- Because the TTL in the previous DNS response was zero, it’s not valid anymore: the browser has to resolve attacker.example.com again by sending a new DNS query
- This time, the resolver returns 192.168.100.1 as an IP address for this name.
- Because the protocol, port number, and name are the same as the initial page, the query for http://attacker.example.com is allowed by the web browser
- Except that the data is sent to, or retrieved from a victim’s internal server, not the initial web site.
This particular attack is actually very old, and all modern web browsers defend against it by caching each name http://attacker.example.com has to resolve. If http://attacker.example.com has to be accessed more than once, the same IP address will be used every time. Failover mechanisms are also intentionally disabled: if the initial IP doesn’t accept HTTP connections any more, web browsers will not fall back to another IP until the whole page is reloaded.
If that’s the case, is this attack just a relic of the past?
The DNS rebinding attack, revived
A few weeks ago, Yunxing Dai and Ryan Resig described a new way to conduct DNS rebinding attacks: FireDrill.
Once a cache is full, inserting new entries requires evicting one or more existing entries first. This holds true for DNS caches as well, including browser DNS caches.
Dai and Resig thus propose a simple yet very efficient way to force a web browser to resolve a domain name it already resolved: simply fill the browser cache by issuing other, unrelated queries.
Filling the cache takes only a few seconds, and we verified that this technique still works on Google Chrome 30 and Firefox 23.0.1.
Their experiment shows how dangerous DNS rebinding attacks can be by abusing a victim’s browser as a proxy. The browser keeps a websocket connection with the attacker machine, and uses rebound domain names to post and retrieve content from internal services in the victim’s network.
Possible defenses against DNS rebinding attacks
How can someone prevent this kind of attack? Targeted hosts are going to see an employee IP rather than the attacker IP, so IP-based filtering doesn’t help much. An HTTP query, however, usually includes the host name the client wants to reach.
If attacker.example.com is being used for a DNS rebinding attack, both the attacker’s web server and the victim’s server are going to see the same header:
Host: attacker.example.com
Web servers receiving a query with an unregistered Host: header commonly serve one of the registered web sites. For example, Apache serves the first one, whereas Nginx serves the one with a listen ..default_server
directive.
One way to prevent this attack is to configure web servers to return no content for queries sent to unregistered virtual hosts. On Nginx, doing so can be as simple as adding this snippet to the configuration file:
server {
listen 80 default_server;
listen [::]:80 ipv6only=on;
server_name _;
return 444;
}
Suspicious HTTP queries will be closed without any further processing. Doing so also proves to be a good way to reduce the system load when botnets are performing blind network scans to find vulnerable web servers.
On the other hand, changing the HTTP server configuration is rarely an option on common devices such as printers, webcams and routers.
Applications servers also rarely check the host name in client requests, on the assumption that they won’t be directly accessed by external, untrusted users.
Not to mention that distributed filesystems, databases and (internal) search engines also commonly expose a HTTP/JSON interface that can become the target of a DNS rebinding attack.
Blocking DNS rebinding attacks with OpenDNS
OpenDNS users can enable the “block suspicious responses” option in their dashboard. This blocks DNS responses containing non-routable IP addresses, and fully protects users from the threats outlined above when browser-based safeguards fall short.
This great feature has been available for free since April 2008, and is an efficient way to neutralize DNS rebinding attacks, provided that the internal network uses these reserved IP ranges.
This kind of attack may come back under the spotlight after the FireDrill paper, so we thought now would be a good time to shine a spotlight on the defense as well!