SubScope — Subdomain Reconnaissance
A subdomain enumeration tool that chains certificate transparency, DNS brute force, HTTP probing, and takeover detection into one clean pipeline
SubScope — Subdomain Reconnaissance
The recon tool I wished I'd had on my first bug bounty.
The Goal
The attack surface of a modern web target is never just one domain. It's an unknown number of subdomains — dev and staging environments, internal APIs, admin panels, forgotten demo sites, legacy apps still running old software. Each one is a potential entry point that won't show up if you only look at the main domain.
My early recon workflow was embarrassing: crt.sh open in one tab, dig in a terminal, maybe subfinder if I remembered to install it. I'd patch together a picture of the target an hour later and still not be confident I'd covered everything.
SubScope fixes that. It chains four discovery methods into one pipeline, probes every live subdomain over HTTP, and outputs a table you can actually reason about — fast.
How It Works
SubScope runs three discovery stages before probing anything:
Certificate Transparency
Every public TLS certificate gets logged. Query %.example.com against crt.sh and you get every subdomain that has ever had a certificate — including subdomains that no longer exist in DNS but might still be interesting. The response includes SANs (Subject Alternative Names), so a single wildcard cert can reveal a dozen subdomains at once.
I've found staging environments on bug bounty programs this way that weren't in any wordlist. Developers can't hide CT logs — every cert ends up there.
DNS Brute Force
The built-in wordlist covers 500+ common prefixes: api, admin, dev, staging, vpn, portal, grafana, jenkins, gitlab, k8s, cdn, mail, and more. SubScope resolves each one concurrently with configurable thread count (default 50). A full 500-word pass against a responsive DNS server runs in a few seconds.
Custom wordlists are supported for deeper coverage — I'll swap in a SecLists wordlist for serious work.
HTTP Probing
DNS resolution tells you a subdomain exists. HTTP probing tells you what's running on it. For each live subdomain, SubScope probes HTTPS first, falls back to HTTP, and captures:
- HTTP status code
- Page title (from the
<title>tag) - Server header (nginx, Apache, CDN, etc.)
- Final URL after any redirect chain
The terminal output color-codes status codes — green for 200s, cyan for redirects, yellow for 4xx, red for 5xx — so a wide scope is scannable at a glance.
Takeover Detection
Subdomain takeovers are some of the most satisfying bugs to find and report. They happen when a subdomain's CNAME points to a third-party service — GitHub Pages, Netlify, Heroku, S3, Azure, Fastly, and others — but the associated account or resource was deleted without cleaning up the DNS record. An attacker can claim that resource and serve arbitrary content from the victim's subdomain.
SubScope checks CNAME records against fingerprints for 18+ known-vulnerable service patterns and makes an HTTP request to confirm the unclaimed error page is present. Even a "potential" match (CNAME matches but fingerprint is inconclusive) is worth manual investigation — some services vary their error messages.
Capabilities
- Query crt.sh for historical subdomain certificates
- DNS brute force with a 500+ prefix wordlist or a custom one
- Concurrent resolution (configurable thread count)
- HTTP/HTTPS probing with status, title, server, and redirect chain
- Subdomain takeover detection across 18+ service fingerprints
- JSON and CSV export for downstream tooling
- Scope file support for multi-domain engagements
What It's Found
On a real bug bounty program (with permission, obviously):
- A
stagingsubdomain running an older application version with a debug endpoint exposed - A
docssubdomain with a CNAME to an unclaimed GitHub Pages site — confirmed takeover, reported and rewarded - An
api-v1subdomain returning verbose error messages including internal stack traces
None of those would have surfaced from a one-time manual check. That's the value of running this systematically every time.
Tech & Tools
| Component | Details |
|---|---|
| Language | Python 3.8+ |
| CT source | crt.sh JSON API |
| DNS resolution | socket module, concurrent via concurrent.futures |
| HTTP probing | requests, follows redirects, HTTPS-first |
| Takeover DB | 18+ service fingerprints in takeover/ |
| Output | Color-coded terminal table, JSON, CSV |
| Architecture | Modular — discovery/, output/, takeover/ as separate packages |
What I Learned
On reconnaissance methodology: Certificate transparency logs are non-optional in a thorough recon pass. I used to treat them as a nice-to-have — now they're the first thing I hit. They catch historical infrastructure that brute force wordlists will never find.
On concurrent DNS: Threading the resolution step made the difference between a tool I'd actually use and one that was too slow to bother with. Getting that right required thinking carefully about thread safety on the results dictionary and not hammering the resolver with unbounded concurrency.
On takeover detection:
CNAME-based takeovers require two confirmations — the CNAME pattern match and the HTTP fingerprint check. The fingerprint step matters because not every CNAME to *.github.io is unclaimed. Skipping it produces too many false positives to be useful.
On building recon tools vs. using them: Writing SubScope forced me to understand what each discovery method actually covers and where it fails. CT logs miss subdomains that never had a cert. Brute force misses anything outside the wordlist. HTTP probing misses internal-only subdomains. No single method is sufficient — the pipeline exists because each one covers the gaps of the others.
Responsible Use
Only test domains you own or have explicit written authorization to test. Most bug bounty programs treat subdomain enumeration as passive recon and include it in scope — but read the program rules and stay within them. When in doubt, ask the program before you scan.
