October 21, 2026
How to Find Subdomain Takeovers Step-by-Step Bug Bounty Guide
A practical, beginner-friendly method to find abandoned subdomains and claim them before anyone else does

By Monika
4 min read
In this series, we've covered finding secrets in JS files, using endpoints to find IDOR and Broken Access Control, and hunting for Business Logic bugs. Today we're going to look at a completely different โ and often easier category: Subdomain Takeover.
Subdomain takeovers are popular in bug bounty because they're easy to understand, easy to automate, and companies take them seriously since they can lead to phishing, cookie theft, and brand damage.
What is a Subdomain Takeover?
Companies create subdomains and point them to third-party services โ AWS S3, GitHub Pages, Heroku, Shopify, Netlify, Azure, and many more. Something like:
blog.target.com โ CNAME โ target.github.io
old-shop.target.com โ CNAME โ someshop.myshopify.comblog.target.com โ CNAME โ target.github.io
old-shop.target.com โ CNAME โ someshop.myshopify.comThe problem happens when the company stops using that third-party service but forgets to remove the DNS record. The DNS entry for "blog.target.com" still points to "target.github.io", but nobody owns that GitHub Pages site anymore.
Since the service is often free or cheap to register, an attacker can simply sign up for that exact service name (target.github.io), and suddenly blog.target.com is serving content that the attacker controls โ fully trusted under the company's own domain.
Step 1: Collect every subdomain of the target
We need a complete subdomain list first. Use multiple tools together for the best coverage:
Tool 1 โ subfinder:
subfinder -d target.com -all -silent -o subs1.txtsubfinder -d target.com -all -silent -o subs1.txtTool 2 โ assetfinder:
assetfinder --subs-only target.com > subs2.txtassetfinder --subs-only target.com > subs2.txtTool 3 โ amass (slower but thorough):
amass enum -passive -d target.com -o subs3.txtamass enum -passive -d target.com -o subs3.txtMerge and deduplicate everything:
cat subs1.txt subs2.txt subs3.txt | sort -u > all_subs.txtcat subs1.txt subs2.txt subs3.txt | sort -u > all_subs.txtStep 2: Resolve DNS records for every subdomain
Now we need to see which subdomains have a CNAME pointing to an external service. dnsx is the best tool for this:
cat all_subs.txt | dnsx -silent -cname -resp -o resolved_subs.txtcat all_subs.txt | dnsx -silent -cname -resp -o resolved_subs.txtThis will show you output like:
blog.target.com [CNAME] [target.github.io]
cdn.target.com [CNAME] [d111111abcdef8.cloudfront.net]blog.target.com [CNAME] [target.github.io]
cdn.target.com [CNAME] [d111111abcdef8.cloudfront.net]Anything with a CNAME pointing to a third-party domain is worth checking further.
Step 3: Check which subdomains are actually dead (not resolving properly)
A dangling subdomain usually shows a specific error page from the third-party service, or fails to resolve the IP entirely. Use httpx to grab status codes and page content:
cat resolved_subs.txt | httpx -silent -status-code -title -o live_check.txtcat resolved_subs.txt | httpx -silent -status-code -title -o live_check.txtLook specifically for:
- "NXDOMAIN" errors when resolving the IP
- Error pages like "There isn't a GitHub Pages site here", "No such app", "The specified bucket does not exist"
- 404s from a service, rather than from the company's own server
Step 4: Use nuclei to automatically detect known takeover signatures
This is the fastest way to confirm a takeover, since the signatures for common services are already built into nuclei 's templates:
nuclei -l resolved_subs.txt -t subdomain-takeover/ -o takeover_results.txtnuclei -l resolved_subs.txt -t subdomain-takeover/ -o takeover_results.txtIf a subdomain matches a known vulnerable fingerprint, this template will flag it directly with the service name (e.g., "AWS S3 takeover possible", "GitHub Pages takeover possible").
Step 5: Manually verify common services by their error signatures
Even without automated tools, you can recognize takeover candidates by their error messages. Here are some of the most common ones:
Service: GitHub Pages
Error text: "There isn't a GitHub Pages site here."
Service: Heroku
Error text: "No such app"
Service: AWS S3
Error text: "The specified bucket does not exist"
Service: Shopify
Error text: "Sorry, this shop is currently unavailable."
Service: Netlify
Error text: "Not Found - Request ID:"
Service: Unbounce
Error text: "The requested URL was not found on this server."Service: GitHub Pages
Error text: "There isn't a GitHub Pages site here."
Service: Heroku
Error text: "No such app"
Service: AWS S3
Error text: "The specified bucket does not exist"
Service: Shopify
Error text: "Sorry, this shop is currently unavailable."
Service: Netlify
Error text: "Not Found - Request ID:"
Service: Unbounce
Error text: "The requested URL was not found on this server."Check manually with curl:
curl -s -I https://blog.target.com
curl -s https://blog.target.com | grep -i "there isn't a github pages site"curl -s -I https://blog.target.com
curl -s https://blog.target.com | grep -i "there isn't a github pages site"Step 6: Claim the resource (only within program scope!)
Once you've confirmed a dangling CNAME and matched it to a specific service's error signature, the next step is registering that exact resource name on the third-party service, following the program's rules.
Example for GitHub Pages:
- Create a new GitHub repository named exactly: target.github.io (if CNAME points there)
- Enable GitHub Pages for that repo
- Add an index.html with your PoC content
- Visit blog.target.com โ if it now shows your PoC page, takeover is confirmed
Important: Many programs require you to not actually serve public content once you've claimed the resource just take a screenshot proving control and report it immediately. Always read the program's rules on subdomain takeovers before claiming anything.
Step 7: Automate the whole recon-to-detection flow
Here's a simple script that ties Steps 1 to 4 together so you can run full subdomain takeover recon on a target in one go:
#!/bin/bash
domain=$1
subfinder -d $domain -all -silent -o subs1.txt
assetfinder --subs-only $domain > subs2.txt
cat subs1.txt subs2.txt | sort -u > all_subs.txt
cat all_subs.txt | dnsx -silent -cname -resp -o resolved_subs.txt
nuclei -l resolved_subs.txt -t subdomain-takeover/ -o takeover_results.txt
echo "Done. Check takeover_results.txt for findings."#!/bin/bash
domain=$1
subfinder -d $domain -all -silent -o subs1.txt
assetfinder --subs-only $domain > subs2.txt
cat subs1.txt subs2.txt | sort -u > all_subs.txt
cat all_subs.txt | dnsx -silent -cname -resp -o resolved_subs.txt
nuclei -l resolved_subs.txt -t subdomain-takeover/ -o takeover_results.txt
echo "Done. Check takeover_results.txt for findings."Run it:
bash subdomain_takeover.sh target.combash subdomain_takeover.sh target.comReporting tips
- Always include the exact subdomain and its CNAME record
- Include the error page screenshot/text that proves the resource is unclaimed
- Explain the real-world risk clearly: phishing pages, session cookie theft (if the subdomain shares a parent domain cookie scope), or reputational damage
- If you claimed the resource as PoC, mention that you did so safely and didn't serve any public content
Final Thoughts
Subdomain takeovers are one of the few bug classes where a pure recon-and-automation approach gets you most of the way there no payload crafting needed. Running this scan regularly across a company's full subdomain list is often enough to catch a takeover before anyone else does, since companies add and remove third-party services constantly.
Add this to your regular recon routine alongside the JS file and endpoint techniques from earlier in this series, and you'll be covering most of the easy, high-impact bug classes that programs actually pay well for.
If this guide helped you, consider buying me a coffee โ โ
it keeps me motivated to dig up and write more bugs like this. Buy me a coffee