Subdomain Takeover: Proof Creation for Bug Bounties

The post about subdomain takeover from last week received great feedback. I decided to follow-up and explain the process of actually taking over "vulnerable" subdomain. I noticed that multiple bug bounty programs started explicitly listing subdomain takeover as in-scope submission. In fact, the rewards are quite high:

HackerOne Subdomain Takeover Report

Although many bug bounty programs are generous, they require you to provide proof of concept that the takeover is indeed possible. In this post, I explain how to verify whether subdomain takeover is possible and provide you with a step-by-step instructions for PoC creation (or SOP). As you may know, subdomain takeover is usually (but not necessarily) associated with cloud providers - the process is explained for top three takeover-prone cloud providers.

UPDATE: Refer to can-i-takeover-xyz as primary project for subdomain takeover PoC. This post acts as extended documentation with screenshots and a deeper explanation.

Before we start, you should be familiar with basic principles of subdomain takeover. There are numerous tools on GitHub which provide subdomain takeover verification:

Eventhough these tools provide nice heuristics about possible subdomain takeover, they sometimes contain false positives due to several restrictions given by the individual cloud provider. In the sections below, the manual verification snippet is provided for each domain. The naming convention for different types of domains follows:

CNAME notation

Amazon S3

Amazon S3 is a storage service that works with concepts of buckets. Buckets are logical units of storage. After you create a bucket, a unique subdomain is generated for it. Sounds familiar?

Amazon S3 follows pretty much the same concept of virtual hosting as CloudFront does. You can read about S3 virtual hosting here. There is only one thing I want to mention. S3 buckets might be configured as website hosting to serve static content as web servers. If the canonical domain name has website in it, the S3 bucket is specified as Website hosting. I suspect that non-website and website configured buckets are handled by separate load balancers, and therefore they don't work with each other. The only difference will be in the bucket creation where correct website flag needs to be set if necessary.

Verification:

CNAME record looks should look something like this:

sub.example.com  60  IN   CNAME sub.example.s3.amazonaws.net

I use the following regexes to test the correct canonical name:

# {bucketname}.s3.amazonaws.com
^[a-z0-9\.\-]{0,63}\.?s3.amazonaws\.com$

# {bucketname}.s3-website(.|-){region}.amazonaws.com (+ possible China region)
^[a-z0-9\.\-]{3,63}\.s3-website[\.-](eu|ap|us|ca|sa|cn)-\w{2,14}-\d{1,2}\.amazonaws.com(\.cn)?$

# {bucketname}.s3(.|-){region}.amazonaws.com
^[a-z0-9\.\-]{3,63}\.s3[\.-](eu|ap|us|ca|sa)-\w{2,14}-\d{1,2}\.amazonaws.com$

# {bucketname}.s3.dualstack.{region}.amazonaws.com
^[a-z0-9\.\-]{3,63}\.s3.dualstack\.(eu|ap|us|ca|sa)-\w{2,14}-\d{1,2}\.amazonaws.com$

To verify whether subdomain takeover may be possible, run:

http -b GET http://{SOURCE DOMAIN NAME} | grep -E -q '<Code>NoSuchBucket</Code>|<li>Code: NoSuchBucket</li>' && echo "Subdomain takeover may be possible" || echo "Subdomain takeover is not possible"

I use httpie instead of curl. You should too.

Takeover:

(Assuming you have AWS account created.)

  1. Go to S3 panel
  2. Click Create Bucket
  3. Set Bucket name to source domain name (i.e., the domain you want to take over)
  4. Click Next multiple times to finish
  5. Open the created bucket
  6. Click Upload
  7. Select the file which will be used for PoC (HTML or TXT file). I recommend naming it differently than index.html; you can use poc (without extension)
  8. In Permissions tab select Grant public read access to this object(s)
  9. After upload, select the file and click More -> Change metadata
  10. Click Add metadata, select Content-Type and value should reflect the type of document. If HTML, choose text/html, etc.
  11. (Optional) If the bucket was configured as a website
    1. Switch to Properties tab
    2. Click Static website hosting
    3. Select Use this bucket to host a website
    4. As an index, choose the file that you uploaded
    5. Click Save

If everything went without any errors, congratulations, the takeover is complete. The files will be present on appropriate URL path. If you uploaded file poc, then it will be http://sub.example.com/poc and so on.

GitHub Pages

GitHub provides free web hosting using their GitHub Pages project. This web hosting is usually used for project's documentation, technical blogs, or supporting web pages to open-source projects. GitHub Pages supports custom domain name in addition to default domain name under .github.io.

Verification

CNAME record looks should look something like this:

sub.example.com  60  IN   CNAME subexamplecom.github.io

I use the following regex to check the correct CloudFront canonical domain name:

^[a-z0-9\.\-]{0,70}\.?github\.io$

To verify whether subdomain takeover may be possible, run:

http -b GET http://{SOURCE DOMAIN NAME} | grep -F -q "<strong>There isn't a GitHub Pages site here.</strong>" && echo "Subdomain takeover may be possible" || echo "Subdomain takeover is not possible"

Takeover

(Assuming you have GitHub account created.)

  1. Go to new repository page
  2. Set Repository name to canonical domain name (i.e., {something}.github.io from CNAME record)
  3. Click Create repository
  4. Push content using git to a newly created repo. GitHub itself provides the steps to achieve it
  5. Switch to Settings tab
  6. In GitHub Pages section choose master branch as source
  7. Click Save
  8. After saving, set Custom domain to source domain name (i.e., the domain name which you want to take over)
  9. Click Save

GitHub Pages Settings

If everything went without any errors, congratulations, the takeover is complete.

Heroku

Heroku is a popular PaaS provider. In our context, it has the same virtual hosting concept as other cloud providers. Various *.herokudns.com subdomain respond with the same set of A records. HTTP Host matters for correct domain resolution (as in other providers). There is also a possibility to upload own certificate to work on a custom domain as well (e.g., GitHub Pages doesn't support this, and thus you cannot have HTTPS enabled with custom domain set).

Verification

CNAME record looks should look something like this:

sub.example.com  60  IN   CNAME subexamplecom.herokudns.com

I use the following regex to check the correct CloudFront canonical domain name:

^[a-z0-9\.\-]{2,70}\.herokudns\.com$

To verify whether subdomain takeover may be possible, run:

http -b GET http://{SOURCE DOMAIN NAME} | grep -F -q "//www.herokucdn.com/error-pages/no-such-app.html" && echo "Subdomain takeover may be possible" || echo "Subdomain takeover is not possible"

Takeover

(Assuming you have Heroku account created.)

  1. Open new Heroku app.
  2. Choose name and region (no effect on takeover).
  3. Push PoC application using git to Heroku. The process is described in Deploy tab.
  4. Switch to Settings tab.
  5. Scroll to Domains and certificates.
  6. Click Add domain.
  7. Provide the domain name you want to takeover, click Save changes.
  8. It might take some time for settings to propagate.

Heroku Custom Domain portal

Readme.io

Readme.io is a popular service for hosting your documentation. As turns out, they also offer custom domain support and more importantly, subdomain takeover is possible using readme.io domains.

Verification

CNAME record looks should look something like this:

sub.example.com  60  IN   CNAME subexamplecom.readme.io

I use the following regex to check the correct CloudFront canonical domain name:

^[a-z0-9\.\-]{2,70}\.readme\.io$

To verify whether subdomain takeover may be possible, run:

http -b GET http://{SOURCE DOMAIN NAME} | grep -F -q "Project doesnt exist... yet!" && echo "Subdomain takeover may be possible" || echo "Subdomain takeover is not possible"

Takeover

(Assuming you have Readme.io account created.)

  1. Go to dashboard.
  2. Set Project Name and its subdomain. Subdomain does not need to match the domain you are trying to takeover.
  3. In left sidebar, go to General Settings -> Custom Domain.
  4. Set Custom domain to the domain you want to takeover.
  5. Click Save.

Readme.io Custom Domain setting

Reporting

In the final report, don't forget to mention all the technical parts. You can also specify the simplified process of PoC creation. Regarding the risks, I urge you to read my another post and alternatively link to it in your report. Since subdomain takeover is rather a new attack vector, you should also offer mitigation strategies for the affected party.

Hopefully, this post provides a good view into inner workings of cloud providers with custom domain names. Many other cloud services (one found here) can be used for PoC creation if CNAME record point to them. The process should be similar to one of the services explained above.

You should bookmark this page and come back during actual PoC creation.

How to find vulnerable CNAME records and more stuff related to subdomain takeover will be described in the future posts. Follow me on Twitter to get it first.

Good luck!

Patrik