Subdomain Takeover

subdomain takeover security-flashcards.com

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:

  1. 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.
  2. 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.