| layout | default |
|---|---|
| title | Labs |
| numbered | false |
These exercises teach subdomain enumeration on fictional zones. -simulate-zone
loads a scenario file and answers every query from a DNS server that subenum
runs on 127.0.0.1 for the length of the scan. No DNS traffic leaves your
machine, so labs are safe in a classroom, a CTF or an air-gapped VM.
Unlike -simulate, which invents results from a hit rate, a lab runs the real
resolver against the scenario. Wildcard detection, -rate pacing, retries, the
reliability guard and the run-quality report all behave exactly as they would
against a live zone, and the results are the same on every run.
You need a subenum binary (see Installation)
and a copy of the repository for the scenario files. For a zero-install
classroom, open the labs in GitHub Codespaces:
the container builds ./subenum, opens this page and the first scenario, and
prints the lab 1 command.
Every lab scans lab.example with the short wordlist examples/labs/words.txt.
Run the commands from the repository root:
L=examples/labs
W="-w $L/words.txt"Each lab ends with questions. The answers are folded underneath; try first.
subenum $W -simulate-zone $L/lab1-first-scan.zone -show-records lab.example-show-records prints the records behind each name.
- How many subdomains did the scan find?
- Which host has an IPv6 address?
shophas the same address aswww. Why?- The scenario file defines
blog, but it is not in the results. Rerun with-type A,AAAA,CNAME. What isblog, and why did the default scan miss it?
Answers
- Five:
www,mail,vpn,jenkinsandshop. www(2001:db8::10).shopis a CNAME (an alias) forwww. The resolver follows the alias and returnswww's addresses. The CNAME run showsCNAME=www.lab.example.blogis a CNAME toghost-lab.example.net, a host that does not exist. The default lookups (A and AAAA) find no address, so the name looks empty. With CNAME lookups, subenum shows the alias and flags itTAKEOVER?=dangling: the alias points at a name nobody has registered. If that were a cloud service, whoever claimed the name would controlblog.lab.example. This is how subdomain takeovers start. See takeover hints in the README.
subenum $W -simulate-zone $L/lab2-wildcard.zone lab.example- What does subenum report, and why does it refuse to continue?
- Rerun with
-force. Which names are reported now, and how does subenum tell them apart from the wildcard?
Answers
- "Wildcard DNS detected": the zone has a
*record, so every name, even a random one, resolves to192.0.2.250. Without filtering, every word in the list would be reported as a "finding". www,portalandintranet. Before scanning, subenum queries several random names to fingerprint the wildcard's answer. With-forceit then drops every result that matches that fingerprint; these three have their own addresses, so they are real hosts. Thewildcard-filteredline counts the rest.
This resolver answers REFUSED once it gets more than 40 queries in a second, as many public and corporate resolvers do.
subenum $W -simulate-zone $L/lab3-rate-limit.zone -stats lab3.json lab.example
subenum $W -simulate-zone $L/lab3-rate-limit.zone -stats lab3-paced.json -rate 30 lab.example- Compare the
refusedcounts of the two runs. - Open both
-statsreports. What is each run'sverdict? - Why can the first run still list some real names, and why should you not trust it anyway?
Answers
- The first run bursts with 100 workers and most of its lookups are refused.
The paced run (
-rate 30, under the resolver's 40-per-second limit) has none. unreliablefor the burst,completefor the paced run.- The first queries arrived before the limit kicked in, so whatever they found is real. But a REFUSED name was never actually checked: it might exist. A run with many refusals says nothing about the names it missed, which is why the report calls it unreliable. Slower is faster here.
subenum $W -simulate-zone $L/lab4-recursive.zone lab.example
subenum $W -simulate-zone $L/lab4-recursive.zone -recursive -depth 3 lab.example- How many names does the flat scan find? How many does the recursive scan find?
- Which name needed
-depth 3? - Try the recursive scan with the bundled 5,000-word list (drop
$W). What happens, and why?
Answers
- Three (
www,dev,corp) against eight. Recursion takes every name it finds and enumerates the wordlist under it, which turns upapi.dev,git.dev,staging.devandvpn.corp, thenadmin.staging.dev. admin.staging.dev, three labels belowlab.example.- subenum refuses to start: with 5,000 words, depth 3 could mean over 100
billion queries. Recursion multiplies the wordlist at every level, so it
asks you to set
-max-queries(or-force) first. Against a real target, that guard is what keeps a scan from becoming a flood.
subenum $W -simulate-zone $L/lab5-failures.zone -timeout 300 -stats lab5.json lab.example- Which outcome lines in the breakdown are non-zero besides
resolvedandnxdomain? - What is the run's
verdictinlab5.json, and what does it mean for the names that failed?
Answers
timeout(legacyandoldnever answer),refused(beta) andother(backupanswers SERVFAIL).degraded: the scan finished, but some names could not be checked. A failed lookup is not "does not exist". Those names need a retry (-attempts) or a different resolver before you can rule them out.
A scenario is a plain text file. Names are relative to the domain you scan,
and # starts a comment:
$DELAY 20ms # every answer waits this long
$REFUSE-ABOVE 50 # answer REFUSED past 50 queries a second
@ A 192.0.2.1 # the scanned domain itself
www A 192.0.2.10
www AAAA 2001:db8::10
shop CNAME www # relative target
blog CNAME ghost.example.net. # absolute target (trailing dot)
*.dev A 192.0.2.99 # wildcard under dev
mail MX 10 mx1 # preference and host (relative or absolute)
@ TXT "v=spf1 -all" # the rest of the line; quotes optional
sub NS ns1.gone.example. # a delegated subzone (scan with -type NS)
slow TIMEOUT # never answered
broken SERVFAIL # also: REFUSED, NXDOMAIN
Start a scenario in examples/labs with a comment saying what it teaches
(# Lab 6: <title>. <what to look for>): subenum mcp shows that comment,
without the lab number and any indented command line, to agents choosing a
scenario.
Names the file does not define are NXDOMAIN. The scenario is the whole world
during a lab, so names outside the scanned domain (such as an external CNAME
target) are NXDOMAIN too. Use the
documentation ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24,
2001:db8::/32) and reserved names such as lab.example, so nothing in a
lab points at a real host.
Scenarios work with every other flag, such as -dL (each listed domain gets
the same zone), -permute, -diff, -format json and -stats, which
records the scenario file under zone. -simulate-zone cannot be combined
with -simulate, -dns-server, -r or -ct, whether they come from the command
line, a SUBENUM_* variable or the config file: a lab run refuses to start
rather than send a query off the machine.