Subdomain Takeover
A subdomain takeover is a vulnerability which allows an attacker to serve content from a subdomain which is not owned by that attacker. The most common situations which make a subdomain takeover possible are:
1) the CNAME record of the affected subdomain points to a domain that can be claimed by an attacker
2) the A record points to an IP address that can be registered by an attacker
In particular the first situation is quite common especially when your subdomains point to hostnames at a cloud provider such as Amazon AWS. So let’s assume you are running a blog on an S3 website bucket at Amazon and you have assigned the hostname myblog.amazonaws.com to this bucket. As you want that this blog should be reachable under your own domain, you have created the domain blog.example.com and a pointer (via a CNAME record) from that domain to myblog.amazonaws.com. Now, if someone opens your domain blog.example.com in a browser, the browser knows it should load the content from myblog.amazonaws.com.
Later, you might decide to shutdown the blog and you decomission the S3 bucket together with its hostname myblog.amazonaws.com. Due to that the Amazon domain becomes available again and someone else could register that domain and create a new S3 bucket for this hostname. Now, a problem may arise. If you forget to also delete the pointer (i.e. the CNAME record) from blog.example.com to myblog.amazonaws.com, the content of the new S3 bucket (that now does not belong to you anymore) will be delivered to everyone who visits your domain blog.example.com.
An attacker could exploit this to deliver malicious content from his/her S3 bucket via your subdomain.
Risks of a Subdomain Takeover
Various risks could be the result of a subdomain takeover.
Bypassing CSRF protection
Probably you have decided to trust all your subdomains and therefore you rely only on SameSite cookies as a CSRF protection. As SameSite protects your web application only from CSRF attacks for cross-site requests, an attacker might use a subdomain takeover to bypass CSRF protection via the compromised subdomains of the same site.
Access and Modify Cookies If you set cookies and if you set the domain attribute to your main domain such as example.com all subdomains will receive this cookie as well. Therefore, an attacker could use a compromised subdomain to get access to all these cookies. Additionally, an attacker could set cookies in a victims browser for other subdomains when a victim visits the compromised domain by specifying the domain attribute.
Phishing
As the content being served from the compromised subdomain comes from a trusted (sub)domain, an attacker could mount a phishing attack to steal credentials from a victim using your site.
Leak of OAuth 2.0 Tokens
OAuth 2.0 is a protocol that allows a user to grant a third party application access to protected resources of the user without giving that application the user’s credentials. Instead, the user authenticates at a trusted site and after that the user is redirected to the application. The redirect contains a token which can be used to access the resources (actually the token is exchange into an access token but that’s not relevant here).
The redirect is only performed if the target URL is trusted. If all subdomains are trusted as target URL, an attacker might be able to get OAuth 2.0 tokens via a target URL which points to a compromised subdomain.
Bypass Content Security Policy
Content Security Policy is a HTTP response header in which you can specify the locations from which content such as JavaScript is allowed to be loaded. This can help mitigating cross-site scripting (XSS) attacks.
If you have whitelisted all of your subdomains, just one compromised subdomain might be sufficient to bypass the protection provided by content security policy.
A, AAAA and CNAME Records
As already mentioned the reason why it might come to a subdomain takeover vulnerability are most often misconfigured CNAME or A records. With the increasing spread of IPv6 also AAAA records could be a reason. Let’s see why this is the case.
When you register a domain you can store various information in form of records for that domain and its subdomains in the Domain Name System (DNS). For instance, the IPv4 address for a domain is stored in the A-record. This records is used by browsers to resolve a human readable hostname into an machine usable IPv4 address. Very similar to the A-record, the AAAA-record (also known as “quad-A”) stores an IPv6 address. The name AAAA was used because an IPv6 address consumes 16 byte (128bit) and therefore is four times larger than an IPv4 address which consumes just 4 byte (32 bit).
Another DNS record type which is interesting for searching for subdomain takeover vulnerabilities is the CNAME record (“Canonical Name Records”). This record is used to map one domain name to another one. The domain name that is mapped to the second domain name is often called “being an alias” for the second domain as it’s an additional name for a domain.
The records for a domain might look like shown in the following example:
NAME TYPE
-------------------------------------------
bar.example.com CNAME foo.example.com
foo.example.com A 192.126.0.10
If you enter the address bar.example.com in the browser, the browser first tries to get the A record of this address. As this address has only a CNAME record which points to foo.example.com, the browser will lookup the A records of this domain. Now, this domains has a A records and the browser opens the IP address 192.168.0.10.
It would also be possible to give both hostnames the same A records. However, the advantage of using CNAME records is that if the IP address changes, you have to update it just for the A record of foo.example.com.
Various other DNS records types exist and you can find a list for instance on Wikipedia. However, to search for subdomain takeover vulnerabilities the CNAME and A records of a domain are the most interesting one.
Get A and CNAME Records on the Linux Command Line With dig
When you’re using Linux its very easy to get the A and CNAME record for a domain as the tool required for this usually installed by default for most Linux distributions. A popular DNS lookup utility you could use is “dig”.
To lookup the DNS records of the domain www.cnn.com you could execute dig as following:
dig www.cnn.com
The output might look like as following:
; <<>> DiG 9.16.1-Ubuntu <<>> www.cnn.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40442
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.cnn.com. IN A
;; ANSWER SECTION:
www.cnn.com. 23 IN CNAME turner-tls.map.fastly.net.
turner-tls.map.fastly.net. 11 IN A 151.101.13.67
The interesting content is located in the answer section which starts with the line “;; ANSWER SECTION:”. In this example the answer to the query is:
www.cnn.com. 23 IN CNAME turner-tls.map.fastly.net.
turner-tls.map.fastly.net. 11 IN A 151.101.13.67
We can see that www.cnn.com is just an alias for the domain turner-tls.map.fastly.net as it has only a CNAME record that points to this domain. In the second line we can see the A record which has the IPv4 address for this domain.
Warning! The output of dig can depend on the nameserver you use as nameservers might behave differently. For instance, sometimes when a queried domain does only have a CNAME record, the answer section could be empty. In that case you should try to query for the CNAME records explicitly.
Specify the Record Type When Using dig
To specify the record type you just need to add the name of the record type as an additional parameter. For instance, to query for CNAME records only, just add “cname” as an additional argument:
dig www.cnn.com cname
Now, the answer section in the output might look like this:
;; ANSWER SECTION:
www.cnn.com. 5 IN CNAME turner-tls.map.fastly.net.
In this answer only the CNAME record is shown.
Instead of “cname” you can also use “a” for the a records, “aaaa” for the aaaa records, “mx” for mx records and so on. If your nameserver supports “any”, all DNS records for a domain will be returned.
Specify the Nameserver When Using dig
If your nameserver does not support “any” you could try to use another nameserver. For instance, you could specify the address of the Google nameserver 8.8.8.8 by adding this address as an additional parameter and by putting an @ sign in front of the address. Here is an example:
dig @8.8.8.8 www.cnn.com any
For instance, when querying the nameserver of my ISP for “any” for the domain gmx.net I get the following answer:
; <<>> DiG 9.16.1-Ubuntu <<>> gmx.net any
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 44888
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;gmx.net. IN ANY
As you can see this response does not contain an answer section. Instead the status of the response is “SERVFAIL” (fourth line). However, if I use the nameserver 8.8.8.8, I get the following (shortened) response:
dig @8.8.8.8 gmx.net any
; <<>> DiG 9.16.1-Ubuntu <<>> @8.8.8.8 gmx.net any
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61609
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 27, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;gmx.net. IN ANY
;; ANSWER SECTION:
gmx.net. 21478 IN A 82.165.229.87
gmx.net. 21478 IN RRSIG A 8 2 86400 20220511065307 [...]
gmx.net. 21478 IN NS ns-gmx.ui-dns.de.
gmx.net. 21478 IN NS ns-gmx.ui-dns.biz.
gmx.net. 21478 IN NS ns-gmx.ui-dns.com.
gmx.net. 21478 IN NS ns-gmx.ui-dns.org.
gmx.net. 21478 IN RRSIG NS 8 2 86400 20220511065307 [...]
gmx.net. 21478 IN SOA ns-gmx.ui-dns.org. dnsadmin.1und1.de. [...]
[...]
Be aware: even if the status is NOERROR the response for “any” might be different for different nameservers. Here is a the result I get from the nameserver of my ISP if I query for “any” for google.com.
dig google.com any
; <<>> DiG 9.16.1-Ubuntu <<>> google.com any
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51127
;; flags: qr rd ra; QUERY: 1, ANSWER: 8, AUTHORITY: 0, ADDITIONAL: 1
[...]
;; ANSWER SECTION:
google.com. 203 IN A 142.250.185.206
google.com. 113 IN AAAA 2a00:1450:4001:82b::200e
google.com. 136 IN MX 10 smtp.google.com.
google.com. 48 IN SOA ns1.google.com. dns-admin.google.com. [...]
google.com. 63471 IN NS ns4.google.com.
google.com. 63471 IN NS ns1.google.com.
google.com. 63471 IN NS ns2.google.com.
google.com. 63471 IN NS ns3.google.com.
And this is the result I get if I use the nameserver 8.8.8.8:
dig @8.8.8.8 google.com any
; <<>> DiG 9.16.1-Ubuntu <<>> @8.8.8.8 google.com any
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10356
;; flags: qr rd ra; QUERY: 1, ANSWER: 18, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com. IN ANY
;; ANSWER SECTION:
google.com. 265 IN A 142.250.185.206
google.com. 265 IN AAAA 2a00:1450:4001:812::200e
google.com. 3565 IN TXT "docusign=1b0a6754-49b1-4db5-8540-d2c12664b289"
google.com. 3565 IN TXT "google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ"
google.com. 25 IN SOA ns1.google.com. dns-admin.google.com. 444806637 900 900 1800 60
google.com. 3565 IN TXT "v=spf1 include:_spf.google.com ~all"
google.com. 3565 IN TXT "docusign=05958488-4752-4ef2-95eb-aa7ba8a3bd0e"
google.com. 3565 IN TXT "MS=E4A68B9AB2BB9670BCE15412F62916164C0B20BB"
google.com. 265 IN MX 10 smtp.google.com.
google.com. 21565 IN NS ns1.google.com.
google.com. 3565 IN TXT "apple-domain-verification=30afIBcvSuDV2PLX"
google.com. 21565 IN NS ns3.google.com.
google.com. 3565 IN TXT "google-site-verification=wD8N7i1JTNTkezJ49swvWW48f8_9xveREV4oB-0Hf5o"
google.com. 21565 IN NS ns4.google.com.
google.com. 21565 IN CAA 0 issue "pki.goog"
google.com. 3565 IN TXT "facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95"
google.com. 21565 IN NS ns2.google.com.
google.com. 3565 IN TXT "globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8=
However, if I explicitly query for the txt record I get the same result from both nameservers:
;; ANSWER SECTION:
google.com. 3600 IN TXT "globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8="
google.com. 3600 IN TXT "google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ"
google.com. 3600 IN TXT "apple-domain-verification=30afIBcvSuDV2PLX"
google.com. 3600 IN TXT "docusign=05958488-4752-4ef2-95eb-aa7ba8a3bd0e"
google.com. 3600 IN TXT "v=spf1 include:_spf.google.com ~all"
google.com. 3600 IN TXT "google-site-verification=wD8N7i1JTNTkezJ49swvWW48f8_9xveREV4oB-0Hf5o"
google.com. 3600 IN TXT "MS=E4A68B9AB2BB9670BCE15412F62916164C0B20BB"
google.com. 3600 IN TXT "docusign=1b0a6754-49b1-4db5-8540-d2c12664b289"
google.com. 3600 IN TXT "facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95"
So as you can see ANY queries can be problematic and it is recommended to query for each records you are interested in explicitly. See also here and here for more details on why ANY queries are problematic.
NXDOMAIN
If you get the status NXDOMAIN in your answer the domain cannot be resolved using DNS. NX stands for “non-existent”. Actually the name “non-existent” can be a bit misleading because you can also get this status even when the domain exists, for instance when the domain exists and only has a CNAME record which points to a domain that cannot be resolved.
From a user perspective it doesn’t make a difference whether this domains does not exist or whether it exists but only has a CNAME records that points to a domain that cannot be resolved. If you enter such a domain in your browser you get the same result in both situations.
However, the latter can result in a subdomain takeover. Therefore, if you get an NXDOMAIN as status in your response to a DNS query, you should verify that this domain is not affected by a possible subdomain takeover.
Using dig to Check for Possible Subdomain Takeover Vulnerabilities
With all that information you should be able to verify that a domain is not affected by a possible subdomain takeover. The steps are:
- Use “dig
a” to check the A records for the domain. If an IP exists that is not assigned, the domain might be vulnerable to subdomain takeover as an attacker might be able to register the address. - Use “dig
cname” to check the CNAME record for the domain. If the CNAME records points to a domain that cannot be resolved, an attacker might be able take ownership of this domain. For instance, if the alias points to a domain that can be registered or if it points to a non-existent S3 website bucket at Amazon AWS. Then, an attacker might be able to also create an S3 bucket and reuse the name.