Wildcard DNS: the silent attack surface
A wildcard DNS record (*.example.com IN A 1.2.3.4) is a thirty-second
operational shortcut that often outlives the team that added it. Five years later it's
catching every probe an attacker throws at you, every typo by your customers, and every
subdomain you forgot you had. The cleanup cost grows with time. Here's why it matters.
What a wildcard actually does
RFC 1034 §4.3.3 specifies the wildcard semantics. The DNS server returns the wildcard's RR for any QNAME that doesn't have a more-specific match. So:
example.com. IN A 192.0.2.10
www.example.com. IN A 192.0.2.10
*.example.com. IN A 192.0.2.20
dig api.example.com → 192.0.2.20 (wildcard match)
dig www.example.com → 192.0.2.10 (specific match)
dig admin.example.com → 192.0.2.20 (wildcard match)
dig totally-fake.example.com → 192.0.2.20 (wildcard match)
There's no NXDOMAIN. Every label your customers, attackers, or random scanners try will resolve to something.
Attack surface multiplier
Three concrete consequences:
- CT log invisibility. A wildcard cert
*.example.comcovers all subdomains. Each subdomain you actually use never appears in CT logs because there's no per-subdomain cert issuance.crt.shshows you a wildcard, not the inventory. You lose passive recon on your own infra. - Subdomain enumeration is free. An attacker probes
jenkins.example.com,grafana.example.com,vault.example.com,prometheus.example.com. All resolve. They land on your default vhost, which is hopefully a 404 — but if your origin is misconfigured, they hit the actual app. - SSO/cookie scope leaks. A cookie set on
.example.comwith a wildcard will be sent to any attacker-suggested label. Combine with a stored XSS on a forgotten internal app: cookie exfiltration scope multiplies.
The vhost-as-a-firewall pattern
A wildcard at the DNS level can be safe if your reverse proxy strictly drops requests whose Host header doesn't match an explicitly-allowed list. nginx:
server {
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444; # close connection
}
server {
listen 443 ssl;
server_name app.example.com api.example.com www.example.com example.com;
# ... real config ...
}
Without the explicit default-server reject, nginx falls through to the first
server block — which may serve your real app to jenkins.example.com.
In that case the wildcard becomes a real attack surface, not a theoretical one.
How UnveilScan detects wildcards
Our subdomain_wildcard checker generates 3 random hex labels (e.g.,
a3f2bc9d.example.com, e7c1a8b2.example.com,
f4d9e2c0.example.com) and resolves them. Heuristic: if all 3 resolve and
share at least one IP, we conclude wildcard. The IP-matching guard avoids false
positives on split-horizon DNS where every random label gets a different answer.
Once we've detected a wildcard, downstream checkers compensate:
dev_staging probes ~30 labels (dev/staging/admin/grafana/...)
but skips them if they all resolve to the wildcard IP unless the response actually
differs from the wildcard response on the HTTP layer. api_surface always
adds a canary label (__unveilscan_canary_nx_404.example.com) and uses its
HTTP response as a baseline to detect catch-all responses.
This dance — canary labels everywhere — is annoying but necessary. Without it, every scanner produces a wall of false positives on wildcard domains.
Cleaning up a long-lived wildcard
You inherited a wildcard. Removal is high-risk. Some labels actively in use, no inventory. Process:
- Phase 1 — observability. Enable per-vhost access logs on your reverse proxy (or just the load balancer). Run for 30 days. The list of distinct Host values seen is your inventory.
- Phase 2 — promote. Convert each Host you saw into an explicit DNS record (A or CNAME). Now every real subdomain has a non-wildcard answer.
- Phase 3 — narrow the wildcard. Change the wildcard to point to a "deprecated" vhost that returns 410 Gone with a Slack webhook for any hit. Run for 30 days.
- Phase 4 — delete. If the deprecated vhost saw zero legitimate traffic for 30 days, delete the wildcard. Now random labels return NXDOMAIN.
Phase 1 takes longer than expected because seasonal traffic exists (a quarterly batch job hits an obscure subdomain once every 90 days). Don't skip the wait.
The subdomain takeover wrinkle
A wildcard hides subdomain takeovers. old-blog.example.com CNAMEs to a
deprovisioned Heroku app: with no wildcard, old-blog returns NXDOMAIN if
the Heroku app is gone. With a wildcard, it resolves to the wildcard IP, hiding the
fact that the CNAME lookup actually fails — until an attacker re-registers the Heroku
app and the CNAME starts succeeding to their infra. Wildcard masks the takeover
until exploitation.
Map your real subdomain inventory
Free Basic scan flags wildcards. Extended runs CT-log enumeration + canary-aware probes for ~30 sensitive labels.
Run a scan