Discovering domains via NS correlation
What are nameservers?
A nameserver (NS) is a specialised server within the Domain Name System (DNS) which translates human-readable domain names into IP addresses.
Essentially, nameservers tell the internet where to find your web server. In this post I will describe a simple technique which can be used to correlate one or more websites using NS data.
Finding nameservers
To find the nameservers for a domain name, the simplest way is to use the dig tool:
$ dig +noall +answer ns deliveroo.com
deliveroo.com. 86400 IN NS mona.ns.cloudflare.com.
deliveroo.com. 86400 IN NS phil.ns.cloudflare.com.
Finding related domains
Here’s something a lot of people don’t realise. Some DNS providers like Cloudflare will assign you a NS pair at the account level. This means that all domain names you add to your account will share the same NS pair.
In the example above, we can see that the domain deliveroo.com has the NS pair mona.ns.cloudflare.com and phil.ns.cloudflare.com. Any additional domains added to Cloudflare by the operators of that account will also share the same NS pair. With a limited number of possible NS pairs, this means a relatively small number of websites are going to share the same pair and thus it becomes trivial to discover other domains owned by the same operator.
The dataset
For this sort of correlation attack, you will need a dataset of domains -> NS. Thankfully, Merklemap provides a DNS record database containing 4 billion+ records. You can download it here.
The dataset is provided in JSONL format and is compressed using xz. The uncompressed raw data is around ~500GB in size. If you just want to extract domain/NS pairs in the format domain,ns1,ns2,ns... you can use xzcat with jq like so:
xzcat dns_records_database.jsonl.xz | jq -r '
select([.results[] | .success?.records?.NS? // empty] | length > 0) |
[.hostname] + [.results[].success?.records?.NS? // empty | .[]] |
join(",")
' > domains.csv
$ du -h domains.csv
1.6G domains.csv
Much more manageable!
Querying the dataset
One way to query the parsed data is using DuckDB. grep will also work but will probably be a bit slower.
NS1="phil.ns.cloudflare.com."
NS2="mona.ns.cloudflare.com."
duckdb -csv -noheader -c "
SELECT column0 AS domain, column1 AS ns
FROM read_csv('domains.csv', header=false)
WHERE list_sort(str_split(column1, ',')) = list_sort(['${NS1}','${NS2}'])
" > results.csv
Looking at results.csv we have ~300 entries. A lot are false positives, but there are some new domains which definitely belong to the same operator:
$ grep -i deliveroo results.csv | cut -d, -f1
deliveroo.de
deliveroo.blog
deliveroo.xn--9dbq2a
deliveroo.studio
deliveroo-packaging.fr
deliveroorideracademy.fr
... 32 more
In a lot of cases you might not be able to correlate one website to another based on just a keyword in the domain name. In those cases you can do things like:
- Fingerprint HTTP responses
- Compare WHOIS information
- Compare technologies used
- DNS similarities
Use cases
Here are some things this sort of correlation attack might be useful for off the top of my head:
- Discovering connected phishing/malware/fraud domains
- Bug bounty: can be used to expand attack surface on programs with generous catch-all scopes
- OSINT: imagine an operator hosts an anonymous website at domain-a.com but also hosts a personal website at domain-b.com using the same NS pair
Wrapping up
This technique is simple but surprisingly effective. I focused on Cloudflare in this post, but they are not the only DNS provider with this sort of behaviour. Happy hunting!