Harvesting Domain Names for a Profit
This article is going to share a novel, opportunistic approach to subdomain takeovers.
I just want to give a quick shout-out to Ian here. Ian is a mentor of mine and my good friend. He is brilliant, AppSec by day, rockstar by night, and he is a breakfast burrito enthusiast. You can find him on social as @imander.
This article is a write-up of “Left4Dead, Harvesting Domain Names for Profit”, a talk from BSides SD 2020 where Ian and I shared our research of this attack vector. So let’s get into it.
Domain Name System(DNS):
Before we dive into Subdomain takeover, we will do a high-level overview of some DNS concepts. DNS is often referred to as “the phonebook of the internet.” Browsers leverage IP addresses to interact with resources across the internet. DNS translates domain names to IP addresses, allowing browsers to load web resources.
A records:
A record or Address record is used to map a domain to an IP address.
For example, imagine the domain name mushroom-madness.com is hosted on a server with IP address 34.211.148.126
. In this example, whatever content is hosted on 34.211.148.126
is served on mushroom-madness.com. The figure below illustrates what happens when a user types mushroom-madness.com
in their browser.
CNAME records:
A CNAME record or Canonical name record is used when a domain/subdomain is an alias to another domain.
For example, imagine the domain name mushroom-madness.com
is hosted using AWS and points to an s3 bucket with the domain name s3-mushroom-us-east2-amazonaws.com
. In this example whatever content is hosted on s3-mushroom-us-east2-amazonaws.com
is the content served on mushroom-madness.com
. The figure below illustrates what happens when a user types mushroom-madness.com
in their browser.
What is a Subdomain Takeover?
A subdomain takeover is an attack that allows a malicious actor to claim a domain previously owned by another user/organization and control the content hosted at this domain. This attack becomes possible if a domain admin does not remove all DNS records that are no longer in use.
An attack like this can be hard to catch if the attacker clones the original site hosted at this domain. The attacker can steal credentials, harvest user data, and much more, ultimately destroying an organization’s credibility.
Commonly Known Method of Attack:
The most common attack vector for a subdomain takeover consists of roughly 3 phases.
1. Harvest DNS names through various means
An attacker may obtain DNS names through extensive reconnaissance of a target organization. One might use various tools to develop a list of DNS names such as OWASP Amass, SubFinder, Shodan, etc.
2. Send requests to web endpoints and pattern match against returned content.
Once we have a list of domain names, we would send a request to each endpoint and look for signs of an abandoned CNAME record.
For example, if we are querying an AWS subdomain, we may hope to receive a response containing the string “No Such Bucket.” We may use a one-liner such as the following to pattern match against responses:
curl http://{DOMAIN_NAME} | grep -E -q '<Code>NoSuch Bucket</Code>|<li>Code: NoSuchBucket</li>' && echo "Subdomain takeover may be possible"
3. If content matches a known hosting provider, then claim the domain (takeover)
If we receive the desired response, we would navigate to the cloud provider and create our CNAME record, giving us control of the domain.
Basic Example:
Let’s see this concept in inaction, imagine our friend Mr. Toad here would like to host a website and decides to deploy it into the cloud.
(Yes, I understand that this is Kirby in the image above, but for this demo, he is Heroku)
So to do so, Toad creates a DNS CNAME record aliasing mushroom-madness.com to abc.herokuapp.com.
Now Toad has his site up and is living the dream...or is it a nightmare?
Unfortunately for Toad, hosting in the cloud ain’t free, and these mushrooms aren’t making him the profit he imagined. He decides to terminate his app and go with his buddy Luigi’s service instead
Oh no! It looks like Toad forgot to terminate his DNS record. The image below is what mushroom-madness.com looks like now.
Bowser has discovered and taken over the domain by registering abc.herokuapp.com in Heroku.
Now, if we navigate to mushroom-madness.com, something isn’t quite right.
Poor Toad’s mushroom empire is ruined. Who will trust purchases on mushroom-maddness.com now?
We have just covered the most commonly known subdomain takeover attack vector. This attack vector is time and resource-intensive and requires brute-forcing a specific target.
A New Approach:
Now that we have a basic understanding of a subdomain takeover and the most commonly known attack method. Let’s get a little more interesting. Our new approach consists of 3 phases as well.
Launch cloud instances and listen for web traffic.
Check if the host header corresponds to the instance.
Profit
Example Of Our New Approach:
Let’s revisit our old pal Toad here. Toad decides to host his website in the cloud again. He launches an instance in AWS, which gets assigned a public IP address.
Toad creates a DNS entry pointing to the newly launched instance.
Again, hosting in the cloud ain’t free, so Toad decides to go with his buddy Luigi’s hosting service instead. Toad terminates his AWS instance, which releases the IP address back to AWS.
A malicious actor (Bowser) launches a few instances in AWS, and one happens to claim the IP address previously allocated by mushroom-madness.com.
It is important to note that this attack vector is entirely opportunistic. Bowser obtains the IP address previously owned by Toad by the luck of the draw. Bowser is not explicitly targeting Toad or mushroom-madness.com. He has no previous knowledge or interest in mushroom-madness.com.
Bowser listens to incoming web traffic and he is in! He realizes he has owned mushroom-madness.com and its customers like our poor buddy Luigi.
So at this point, you are probably thinking, yeah, cool cartoon example, but this can not possibly work in real life, right?
We decided to test this theory for ourselves and ran an experiment for just under three days.
Weapon of Choice:
As you can see, our weapon of choice is a “zombie cluster”, a cluster of nano instances with public IP addresses deployed in AWS, configured as basic web servers. Extremely simple but incredibly effective.
1. Deploy instances using Terraform in AWS autoscale groups.
We deployed several instances using Terraform. Terraform deployed each instance with a default name: “SD-takeover.” The instances ran an installer script upon launch. The installer script set up a web server that supports the following steps.
2. Listen to well-known web ports for web traffic(80,8080, 443,8443).
We wrote a Ruby script that ran as a web server that listens for incoming traffic over some standard web ports.
3. Parse host headers and determine if the hostname resolves to the current instance public IP address
Our webserver parsed incoming request headers and verified the hostname in the request was resolved to the instance IP address.
4. If the host resolves, rename the instance to the claimed DNS name.
If the instance receives a hostname in the request header, we will do a DNS lookup and check that the record resolves our ec2 instance IP address. If the name matches, the instance name will change to the hostname it received.
Renaming the instance is not functionally required, but we used this naming convention to assist us in the cleanup stage.
5. Obtain TLS certificate
Since we now own the domain, we can provision our own TLS certificate for the domain. We used LetsEncrypt as it was the most straightforward for us at the time, but it is entirely the attacker’s choice.
6. Terminate unsuccessful instances after the specified time limit
The instances would run for 10 minutes. After 10 minutes, a Terraform script would tear down the instances with the default name “SD-takeover,” leaving the instances that have claimed DNS names (Our zombie cluster bombs) running.
7. Pwnage
After running the experiment for just under three days, we were able to find 52 domains subject to this takeover technique. The cost of running the experiment was approximately $48, so each “kill” was just under a dollar. There are a few variables we could switch up to optimize this attack vector in various ways, including cost.
As you can see we yielded interesting results while testing the theory.
Impact of Exploitation:
Now that we have observed a different approach to subdomain takeovers let’s discuss what that could mean for an organization that falls victim to this attack.
If an attacker gains control of a domain, the attacker can control the content hosted on that domain.
User Account Takeover:
One way an attacker might accomplish this is via a typical phishing example.
Send a URL to a victim that points to the login page.
Capture user login credentials when they type them into the form
It is worth noting that often times Password managers may automatically fill the user credentials before the user can even decide not to.
3. Leverage stolen credentials to authenticate as the victim
Classic subdomain takeover exploits do not always give full server-side control. If you do not have server-side control gaining access to protected cookies usually requires a chained exploit. Since we obtained the actual instance with the IP associated with the domain name in our attack method, the attacker will always have server-side control.
Having Control Over The DNS Name Enables Attackers To Obtain Valid TLS Certificates:
Most browsers will warn the user if the website is not TLS protected, and there is a form to enter credentials. However, in our attack scenario, users will not be warned. We will have a TLS certificate because we have complete control over the Domain.
Malware can be delivered via the takeover website using javascript injection or fake software downloads if the domain taken over has a page that hosts downloadable software.
We could also leverage this domain to execute compelling phishing campaigns to deliver malware and gain further access.
Numerous other exploit paths can be leveraged that may depend on the traffic hitting the claimed endpoint.
DNS Hygiene:
Ensure that DNS is actively managed and controlled. Ensure that all records that are no longer in use are removed. This can be automated using infrastructure as code to decrease the possibility of human error, hopefully.
Leverage certificate authority authorization (CAA records). It is essentially specifying which Certificate Authority (CA) can authorize certificates for a particular domain.
If implementing CAA, consider using extended validation certificates. Extended validation adds a procedure that verifies that you are the owner of this domain before granting you the certificate. It can alert you if there is a failed attempt to request a certificate.
Cloud Provider Best Practices:
Use load balancers instead of public instances.
Infrastructure as code (did we already mention say that?). Having an automated procedure, or possibly using a Terraform script to provision your instance infrastructure upon use, and a teardown script that will destroy the DNS record BEFORE releasing the IP is a useful solution.
HTTP and HTTPS DNS health checks with string matching. Health checks with string checking can be more effective in protecting against this type of attack than status codes. (i.e., 200 codes, etc.)
Imagine that Toad sends a health check to mushroom-madness that expects “All good in the mushroom-hood”. After Bowser has owned the instance associated with mushroom-madness. Bowser might send back something like “Status 200, OK”. When comparing these strings, Toad’s health check will error and notify him that something is up.
Namespaced Environments:
Separating environments would limit the scope of session cookies.
Targeting users would require production DNS names to be taken over.
HTTP Strict Transport Security:
HSTS implementation with preloading would severely limit this attack vector. We can only provision a TLS certificate after seeing a valid hostname in a HTTP request in our current attack method. If HSTS is implemented, our attack success rate will decrease dramatically, only allowing us to obtain the hostname from first-time webserver visitors of that domain or visitors who are visiting the server after HSTS policy max-age has expired.
There is Always Room For Improvement:
There are many ways we could have improved our results. We could have worked to optimize our price per kill ratio by using spot instances. If we were to make this change to our experimental design, in attempts to make up for the number of available instances, we would launch in different regions. This modification would not only help us optimize cost but launching the instances in different regions may even increase our odds of obtaining an IP address associated with an abandoned domain.
Using the server logs, we could have found the average time it took to receive a web request and used this time interval to reduce the time our web servers ran. Reducing the time the servers ran would allow us to tear down and redeploy our zombie cluster (our instances) more frequently and increase our success odds.