A Guide To Subdomain Takeovers 2.0
Six years after my initial version of "A Guide To Subdomain Takeovers" was published, I am excited to share further insights and developments from the world of subdomain takeovers. Much has happened since then: I have resumed competitive swimming, completed a university degree, and my hair has started thinning. However, one thing remains unchanged: subdomain takeovers are just as relevant as they were six years ago.
The aim of this blog post is to provide a general understanding of subdomain misconfigurations, supplemented with up-to-date resources and tools. This article assumes that readers have a basic understanding of the Domain Name System (DNS) and know how to set up a subdomain.
1. Understanding subdomain takeovers
2. Identifying vulnerable services
3. Examples of vulnerable and secure services
5. Automating the process of finding subdomain takeovers
6. Exploiting subdomain takeovers
7. Final notes on best practices for reporting subdomain takeovers
Understanding Subdomain Takeovers
Scenario
First, a quick, fictitious scenario to get you up to speed with subdomain takeovers. Let's assume that example.com is the target in a bug bounty program. While enumerating all the subdomains belonging to example.com—a process we will explore later—we stumble across subdomain.example.com, which is pointing to a third-party service, namely GoHire. GoHire is a hiring platform that allows users to set custom domains for their branded job boards as documented here.
In this instance, we can confirm that the subdomain is pointing to GoHire by examining the subdomain's DNS records. This method is not always reliable, particularly when services employ solutions like Cloudflare for DNS management. For simplicity's sake, let us assume that our example is pointing directly to custom.gohire.io, as per the GoHire documentation.
$ host subdomain.example.com subdomain.example.com is an alias for custom.gohire.io. |
When navigating to subdomain.example.com, we encounter the following 404 error page.
At this point, some hackers' senses start tingling. The 404 page suggests that no content is being served under the top-level directory, prompting us to try adding this subdomain to our personal GoHire instance.
Bingo! We have found a subdomain takeover and seized control of the subdomain. How did this happen? The company likely deleted their GoHire instance but overlooked removing the corresponding DNS records. This oversight allowed us to claim ownership of subdomain.example.com since GoHire only requires the DNS to point to custom.gohire.io.
To prevent such incidents, it is crucial for companies to remove DNS entries before deleting their GoHire instance. Additionally, services such as GoHire could enhance security by implementing a domain ownership challenge and/or per-user randomly generated subdomains. Domain ownership challenges often work by generating a random string that GoHire users must add as a DNS TXT record to the target subdomain. GoHire checks that this DNS TXT record exists on the domain before allowing the user to host their GoHire page, ensuring that the user indeed controls the subdomain before allowing it to point to their service.
Now that the custom subdomain has been added to our GoHire project, we can see that our instance is being served on subdomain.example.com. For demonstration purposes, we have constructed an HTML document with a code comment containing our HackerOne handle in a random, hidden directory (e.g., /akjehajkgehagjeahgjkehakghjaehg.html).
$ curl https://subdomain.example.com/akjehajkgehagjeahgjkehakghjaehg.html <!-- hackerone.com/edoverflow.com --> |
Constructing the proof of concept in this subtle manner, rather than on the index page of the hijacked subdomain, avoids damaging the brand's reputation. This is considered good practice and is appreciated by bug bounty program owners. There have been incidents in the past of hackers posting messages similar to "HACKED BY EDOVERFLOW" or hosting images on the index page, which can come across as malicious defacement, even if it is well-intended.
Identifying Vulnerable Services
How do I know if a service is vulnerable?
We discussed GoHire as an example of a vulnerable service, but how can we identify other services that leave customers vulnerable to such oversights? There are a few solutions available.
Community Resources
The straightforward approach is to consult "Can I take over XYZ?". This community-maintained GitHub repository tracks services vulnerable to subdomain takeovers. The repository has largely evolved into a discussion board where the issue tickets allow for more open discussion surrounding the nuances of performing subdomain takeovers against particular services.
Hands-on Testing
For services not documented in the aforementioned GitHub repository, a more hands-on approach is required. Check if the service conducts domain ownership verification. Sign up for the service and attempt to add your own subdomain. Does the service challenge you in the process? If not, it is likely the service allows for subdomain takeovers. You could simulate a subdomain takeover by trying to "claim" ownership of one of your own subdomains that points to a deleted project or account.
Examples of Vulnerable and Secure Services
At the time of writing, "Can I take over XYZ?" lists 76 services, some of which are vulnerable, while Nuclei includes 72 templates for identifying vulnerabilities in these services. These lists aren't exhaustive, but they're a great place to start.
Let's examine two case studies: one involving a vulnerable service and another in which subdomains are protected from hijacking.
Vulnerable Service: ReadMe
ReadMe is a tool for creating interactive documentation. Each project is assigned a *.readme.io subdomain, with the option to configure custom subdomains. As shown in the configuration panel below, adding a custom subdomain involves simply adding a CNAME record to the respective *.readme.io subdomain.
When creating a new project, ReadMe prompts the user to specify their desired *.readme.io subdomain. If a user deletes their project but forgets to remove the corresponding DNS record for their custom subdomain, claiming the subdomain is straightforward: just create a new ReadMe project using the subdomain specified in the CNAME records (e.g., edoverflows-test-project.readme.io as seen in the screenshot below).
Secure Service: Okta
A case study illustrating a secure approach is Okta, an identity and access management service. Okta prevents subdomain takeovers from occurring by offering custom domains that require ownership verification through the use of unique, randomly generated strings that users must insert into a DNS TXT record. This process ensures that only users with access to the DNS configuration of a subdomain can link it to their Okta instance. As a result, even if outdated DNS records still point to Okta, the subdomain remains secure and cannot be hijacked. This is because an attacker would need to update the subdomain's DNS configuration with their own newly generated Okta verification string to prove control over the subdomain's DNS settings, thereby demonstrating genuine domain ownership.
Enumerating Subdomains
You know how to identify a vulnerable service; however, now you need to actually discover as many in-scope subdomains as possible. To achieve this, two forms of subdomain enumeration can be used in tandem.
Active Enumeration
This method involves directly interacting with the target domain to gather information about its subdomains. Tools like MassDNS, puredns, and dnsgen perform active brute-force attempts to discover subdomains. These tools systematically probe the domain's DNS infrastructure for any associated subdomains by querying different DNS records (such as A, AAAA, CNAME, etc.) and analyzing the responses received.
Passive Enumeration
Passive enumeration gathers information without directly interacting with the target domain. This approach typically involves leveraging third-party sources such as search engines, databases, certificate transparency logs, internet data brokers, and other repositories where subdomains may be inadvertently disclosed or indexed. Tools like Amass and Sublist3r automate the collection of such data from a range of passive (and active!) sources, providing a comprehensive list of subdomains associated with the target domain.
Automating the Process of Finding Subdomain Takeovers
Due to the predictable nature of subdomain takeovers, it's possible to automate detection and exploitation.
Automation with Nuclei
One powerful tool for automating the detection of subdomain takeovers is Nuclei. Nuclei is a vulnerability scanner that supports custom scanning templates, including those designed specifically for identifying subdomain takeovers. Templates can be found under the nuclei-templates/http/takeovers folder.
$ ls ~/nuclei-templates/http/takeovers/ aftership-takeover.yaml ghost-takeover.yaml netlify-takeover.yaml tave-takeover.yaml agilecrm-takeover.yaml gitbook-takeover.yaml ngrok-takeover.yaml teamwork-takeover.yaml aha-takeover.yaml github-takeover.yaml pagewiz-takeover.yaml tilda-takeover.yaml [...] |
Of course, the selection of services in that template folder is not exhaustive. Therefore, it is advantageous to be able to design custom templates for new vulnerable services that you discover. Usually, this will entail adding a word matcher for a particular string associated with the 404 page of the vulnerable service. For instance, with Wix, the template needs to search for Error ConnectYourDomain occurred and wixErrorPagesApp in the HTTP response.
$ cat ~/nuclei-templates/http/takeovers/wix-takeover.yaml id: wix-takeover info: name: Wix Takeover Detection author: harshinsecurity,philippedelteil severity: high description: This subdomain take over would only work on an edge case when the account was deleted. You will need a premium account (~ US$7) to test the take over. reference: - https://github.com/EdOverflow/can-i-take-over-xyz/issues/231 metadata: max-request: 1 tags: takeover,wix http: - method: GET path: - "{{BaseURL}} matchers-condition: and matchers: - type: dsl dsl: - Host != ip - type: word words: - 'Error ConnectYourDomain occurred' - 'wixErrorPagesApp' condition: and - type: status status: - 404 # digest: 4a0a00473045022064e66b00c42664cbb39163c28 85d293e24428ccf16cd22c4b474e0ab00dbe36e0221009744fe99f306c b4dd01d034c28b7dbdf162cc8818f3f761d729eef8b13473f36:922c64590222 798bb761d5b6d8e72950 |
Writing a Nuclei Template for a New Vulnerable Service
Let us walk through the process of writing a Nuclei template for a new vulnerable service. We will use GoHire as our example. The string to match here is: "You may have followed an invalid link, or the job you are looking for has been archived."
1. Install Nuclei: If you have not already, you can install Nuclei by following the instructions on the Nuclei GitHub page.
2. Create a new template file: Navigate to your Nuclei templates directory and create a new YAML file, for example, gohire-takeover.yaml.
3. Define the template metadata: Start by adding the metadata for your template. This includes the template ID, information about the template, and any relevant tags.
id: gohire-takeover info: name: Gohire.io Takeover Detection author: yourname severity: high description: Detects potential subdomain takeover on Gohire.io by matching a specific error message. reference: - https://github.com/EdOverflow/can-i-take-over-xyz/issues/403 tags: takeover, gohire |
4. Specify the HTTP request: Define the HTTP request that Nuclei should perform to check for the vulnerability.
http: - method: GET path: - "{{BaseURL}}" |
5. Add matchers: Add matchers to check for specific conditions in the HTTP response. For GoHire, we want to look for the string "You may have followed an invalid link, or the job you are looking for has been archived" in the HTTP response.
matchers-condition: and matchers: - type: word words: - 'You may have followed an invalid link or the job you are looking for has been archived' condition: and - type: status status: - 404 |
6. Run the template: With your new template created, you can now test it using Nuclei against a known, vulnerable host (e.g., vulnerable.example.com).
$ echo "http://vulnerable.example.com/" | nuclei -t ~/nuclei-templates/http/takeovers/gohire-takeover.yaml |
You have successfully created a new Nuclei template for a service not included in the default list. This gives you a competitive edge, as you can now scan for previously undocumented subdomain takeovers that other Nuclei users might overlook.
Exploiting Subdomain Takeovers
Now that you control a subdomain belonging to the target and have confirmed your ability to serve content on it, what's the next step?
As stated earlier, it is considered best practice to host an HTML file on a hidden path, containing a secret message or your HackerOne handle within an HTML comment, rather than publishing content on the index page for the world to see. This approach should be sufficient to demonstrate the issue when initially contacting the program about your finding. Only after receiving permission from the team should you proceed to escalate the issue and fully illustrate the overall impact of the vulnerability. In most cases, the team should already be aware of the potential impact.
Assuming you are cleared to advance with exploitation and wish to explore different attack avenues, we need to explore how the subdomain interacts with other services and potentially vulnerable components belonging to the bug bounty program.
Cookies
One significant aspect to consider is the subdomain's ability to modify cookies scoped to the main domain (example.com). If the cookies are not adequately protected, this capability could potentially lead to session hijacking on the main domain.
From output.jsbin.com, we can set cookies for jsbin.com.
Similarly, if jsbin.com had a non-HTTPOnly cookie scoped to all of its subdomains, we could read it from output.jsbin.com.
If the main domain is susceptible to session fixation, setting a malicious session cookie can enable persistent session hijacking. However, in cases where victims have logged in, their browser likely already has a session cookie which is HTTPOnly and hence protected from being overwritten. There are several clever tricks that can be leveraged to create duplicate cookies with the same name with higher precedence over the original ones, which can be found on this slide.
Cross-Origin Resource Sharing (CORS)
Cross-Origin Resource Sharing (CORS) plays a crucial role in allowing or restricting cross-origin requests. Applications often configure CORS policies, assuming subdomains are trusted entities. When hijacking a subdomain, checking for CORS headers—effectively detected by tools like Burp Suite Pro—becomes pivotal. Exploiting misconfigured CORS policies can potentially allow for unauthorized data extraction, including sensitive authenticated data, from the main application.
GET /foobar HTTP/1.1 Host: example.com Origin: https://vulnerable.example.com [...] HTTP/1.1 200 OK [...] Access-Control-Allow-Origin: https://vulnerable.example.com [...] |
OAuth Whitelisting
Similar to CORS, OAuth implementations often allow specific callback URIs. Exploiting allowed subdomains can enable you to redirect users during OAuth flows to your controlled subdomain, potentially exposing their OAuth tokens.
OAuth Whitelisting Exploitation Example
Let's say there's a web application using OAuth 2.0 for user authentication. The implementation allows any subdomain of the application's domain in the redirect URI whitelist. For example, the application's domain is example.com, and the allowed redirect URIs include *.example.com.
1. The attacker registers a subdomain like attacker.example.com (perhaps the main site allows subdomains to be created for different purposes, like user blogs or workspaces), or maybe the attacker performs a subdomain takeover attack to take control of an existing subdomain.
2. The attacker prepares a malicious service on attacker.example.com. This service is crafted to capture OAuth tokens when a user is redirected to it, and could be as simple as a HTTP server with logs enabled.
3. The attacker creates a malicious link with the redirect URI pointing to the compromised subdomain. The victim user tries to authenticate with a third-party OAuth provider (e.g., Google, Facebook) by logging into the legitimate web application at www.example.com, but uses the attacker's crafted link with the malicious redirect_uri.
4. The OAuth provider checks if the redirect_uri is allowed based on the whitelist set by the legitimate app. Since attacker.example.com is a valid subdomain under *.example.com, the request is approved.
5. After successful authentication, the OAuth provider redirects the victim back to attacker.example.com, along with the OAuth token in the query parameters or fragment (depending on the OAuth flow, e.g., Authorization Code or Implicit).
6. The attacker’s malicious server captures the OAuth token or authorization code. With this, the attacker can now access the victim's account, impersonating the victim in the legitimate app.
This type of attack leverages poor validation of redirect URIs, which can enable an attacker to hijack OAuth tokens and compromise user accounts.
Content-Security Policies (CSP)
Content-Security Policies (CSP) are a layer of defence, restricting which hosts can execute client-side code within the application's context. If your subdomain is included in the CSP allow list, it could be leveraged to bypass these security measures and execute malicious client-side code.
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' vulnerable.example.com; style-src 'self' 'unsafe-inline' vulnerable.example.com; img-src 'self' data: vulnerable.example.com; font-src 'self' data: vulnerable.example.com; connect-src 'self' api.example.com vulnerable.example.com; frame-src 'self' vulnerable.example.com; object-src 'none'; base-uri 'self'; form-action 'self'; |
Cross-Site Request Forgery (CSRF)
Modern browsers include the SameSite attribute to all cookies by default, which eliminates the majority of CSRF attack scenarios. With a subdomain under our control, we can send HTTP requests to the main site with all original cookies attached. This is because subdomains are considered "same site".
Final Notes on Best Practices for Reporting Subdomain Takeovers
Things to Avoid
Besides website defacement, there are two things you should avoid when submitting subdomain takeovers. The first is submitting potential takeovers. Unless the bug bounty program states they are interested in receiving potential subdomain takeovers, it is best advised to hold on reporting these findings until the takeover can be reliably demonstrated. There are many instances where subdomains appear to be vulnerable, but aren't. The best way to know for sure is to just try it. Program owners don't have time to research each potential subdomain takeover without a proof of concept.
Another area of contention is keeping a copy of the proof of concept page on the Wayback Machine. I have been informed this practice is not always appreciated by bug bounty programs and is best avoided.
Conclusion
Subdomain takeovers are still a big deal, even after six years. We have covered the basics of identifying vulnerable subdomains, explored tools like Nuclei for automation, and highlighted real-world examples like GoHire and Okta. With these insights, you are better equipped to tackle subdomain takeovers in the wild. Happy hunting and safe hacking!
The 8th Annual Hacker-Powered Security Report