Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

subenum

Async subdomain enumeration and recon orchestration for authorized security testing.

Python 3.9+ License Version Pipeline

subenum maps hostnames that belong to a target, then enriches them until you can decide what is worth touching next: live HTTP, TLS names, ASN/CDN, third-party CNAMEs, historical URLs, and optional nuclei findings.

It started as a sequential wordlist brute-forcer. It is now a Python-native enumerator that optionally shells out to the same open-source stack used in modern bug bounty pipelines (ProjectDiscovery, OWASP Amass, tomnomnom, Assetnote).

Use it only on assets you own or have written permission to test.

  passive OSINT ──► DNS resolve ──► HTTP / TLS ──► crawl / ports ──► optional nuclei
       │                 │               │
   crt.sh, OTX,      wildcard         httpx or
   subfinder,        filter, AXFR,    native probe
   CT, Wayback       SRV, SPF, PTR    + Team Cymru
pip install -r requirements.txt
python3 -m subenum example.com --deep --json -o results.json

Why this rewrite

The original subdomenum.py walked a hardcoded list of ~1,000 labels, one DNS query at a time, and printed “valid”. That misses how subdomain discovery actually works in 2026:

Technique Why it exists In subenum
Certificate Transparency (RFC 6962) Publicly logged certs leak hostnames operators forget crtsh, certspotter
Passive DNS / OSINT Historical resolutions survive after a name is unpublished OTX, HackerTarget, urlscan, Anubis, RapidDNS, ThreatMiner
Wayback / Common Crawl CDX Old URLs encode forgotten environments native Wayback + optional gau / waybackurls
Wildcard-aware brute force Catch-all DNS (*.example.com) poisons naive brute force random-label probes, then IP-set filter (puredns-style)
Zone transfer (AXFR) Some NS still allow RFC 1035 transfers queried against the zone’s own nameservers
NSEC walking DNSSEC zones without NSEC3 publish the next name in the zone (RFC 4034; NSEC3 is RFC 5155 and is not brute-forced) --dnssec, authoritative NS only
TLS SAN harvest The certificate the server presents often names siblings --tls or tlsx
HTTP enrichment Status, title, stack, WAF, favicon hash native probe or httpx
ASN / CDN Team Cymru IP-to-ASN plus CNAME provider inventory on by default after resolve

Root is not required. The old README said to run as root; that was unnecessary.


Install

Python 3.9+ (asyncio, aiodns, httpx, dnspython, rich).

git clone https://github.com/alexbieber/subenum.git
cd subenum
python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt

Optional, on your PATH:

python3 -m pip install -e .
subenum example.com

The old entry point still works: python3 subdomenum.py example.com.

Optional Go toolchain

Native mode is complete without extra binaries. If you already use the bounty stack, subenum will call whatever is installed:

python3 -m subenum --list-tools
go install github.com/projectdiscovery/pdtm/cmd/pdtm@latest
pdtm -ia

Quick start

# Default: passive OSINT + wordlist brute + DNS intel + AXFR + ASN
python3 -m subenum example.com

# Enrichment: permutations, recursive labels, HTTP, TLS, PTR, DNSSEC
python3 -m subenum example.com --deep

# Use installed subfinder/httpx/katana/gau/… when present
python3 -m subenum example.com --deep --chain

# Deep + chain + port scan + common-path fuzz + nuclei (if installed)
python3 -m subenum example.com --full

# Pipeline-friendly (one hostname per line on stdout)
python3 -m subenum example.com --silent | sort -u

# Never shell out
python3 -m subenum example.com --no-external

example.com is the IANA documentation domain. Use it to verify the install, not as a stand-in for someone else’s production.


Pipeline

targets
   │
   ├─ passive  crt.sh · certspotter · OTX · Wayback · urlscan · Anubis · …
   ├─ binary   subfinder · amass · assetfinder · findomain          (if on PATH)
   ├─ dns      NS/MX/TXT/SPF/SRV harvest · AXFR · optional NSEC
   ├─ brute    1,230-label wordlist, concurrent A/AAAA/CNAME
   ├─ mutate   --permute / --recursive
   ├─ resolve  trusted resolvers, wildcard filter, optional DoH
   ├─ tls      SAN / issuer / expiry
   ├─ http     status, title, tech, CSP/HTML extract, favicon mmh3
   ├─ intel    Team Cymru ASN, CDN, private IPs, CNAME providers
   ├─ crawl    gau · waybackurls · katana · hakrawler               (--chain)
   ├─ ports    naabu or nmap on configured web ports                (--naabu)
   ├─ paths    ffuf / feroxbuster against a small common-path list  (--ffuf)
   └─ nuclei   community templates on live URLs                     (--nuclei)

stdout is for results. Logs and the summary panel go to stderr, so this composes:

python3 -m subenum example.com --silent | httpx -silent

Usage

python3 -m subenum [domain] [options]

Target

Flag Meaning
domain or -d Single target (example.com, URL, or IDN)
-l FILE One domain per line
stdin Used when no domain is given and stdin is not a TTY
-c FILE JSON config overlay (resolvers, concurrency, rate, ports)

Discovery

Flag Meaning
-w FILE Wordlist (default: packaged common.txt)
-t N Concurrency (default 100)
--timeout SEC Network timeout
--resolvers Comma-separated DNS servers (default 1.1.1.1,8.8.8.8,9.9.9.9)
--sources / --exclude-sources Passive source allow/deny list
--passive-only / --brute-only Skip brute or skip OSINT
--permute Label permutations (dev-api, numeric neighbors)
--recursive Common labels under names already found (staging.api.)
--rate N Max DNS queries per second (0 = unlimited)
--doh Retry unresolved names over Cloudflare DNS-over-HTTPS
--no-validate-resolvers Keep resolvers even if they fail the sanity check

Enrichment

Flag Meaning
--deep permute + recursive + probe + TLS + PTR + DNSSEC
--chain Prefer installed PD/OWASP binaries + crawl
--full --deep + --chain + --naabu + --ffuf + --nuclei
--probe HTTP/S, HTML/CSP host extraction, favicon hash
--tls Certificate SAN harvest
--ptr Reverse DNS on discovered IPs
--dnssec NSEC walk on authoritative NS (skips NSEC3)
--crawl gau / waybackurls / katana / hakrawler
--naabu Port scan via naabu, or nmap if naabu is missing
--ffuf Common-path discovery (small packaged wordlist)
--arjun Parameter discovery
--nuclei Template scan of live URLs
--ports HTTP / scan ports (default 80,443)
--no-axfr --no-intel --no-resolve --no-apex --no-external Turn pieces off

Output

Flag Meaning
-o FILE Write .txt / .json / .jsonl / .csv by suffix or flag
--json --jsonl --csv Structured stdout
--filter-status 200,403 Keep those HTTP codes
--private-only RFC 1918 / loopback / link-local only
--has-http Keep hosts that answered HTTP/S
-s --silent Hostnames only
-v Per-source counts
--list-sources Built-in OSINT list
--list-tools Installed vs missing external binaries

Passive sources

No API key (on by default)

Source Origin
crtsh crt.sh Certificate Transparency
certspotter SSLMate Cert Spotter issuances
hackertarget Host search API
otx AlienVault OTX passive DNS
wayback Internet Archive CDX
urlscan urlscan.io
anubis jldc.me / Anubis index
threatminer ThreatMiner domain API
rapiddns RapidDNS index

Enabled automatically when the environment variable is set

Variable Source
VIRUSTOTAL_API_KEY or VT_API_KEY VirusTotal subdomains
SECURITYTRAILS_API_KEY SecurityTrails
GITHUB_TOKEN GitHub code search (text-match)

Failed sources are reported and skipped. One timeout does not abort the scan.


External tools

subenum orchestrates; it does not vendor fifty Go binaries. shutil.which decides whether to exec. Arguments are passed as a list (shell=False).

Stage Binaries
Enum subfinder, amass, assetfinder, findomain
Probe httpx, tlsx
Crawl katana, gau, waybackurls, hakrawler
Ports naabu, nmap
Paths ffuf, feroxbuster
Params arjun
Scan nuclei

Not wrapped: sqlmap, dalfox, hydra, Metasploit. Those are exploitation tools. If a program allows that testing, pipe hostnames out of subenum yourself.

gitleaks and trufflehog appear in --list-tools because they are part of the usual secret-scanning set; they are not auto-run against a bare domain (they need a repo or files).


Output

On a TTY: a host table plus a summary panel (unique/private IPs, AXFR, ASNs, CDNs, extra SANs, tools used).

When stdout is redirected: one hostname per line.

JSON shape (abridged, from a live run against example.com):

{
  "tool": "subenum",
  "version": "2.2.0",
  "domain": "example.com",
  "count": 2,
  "stats": {
    "hosts": 2,
    "unique_ips": 4,
    "http_live": 2,
    "axfr": "refused",
    "asns": { "13335": "CLOUDFLARENET - Cloudflare, Inc., US" },
    "cdns": { "Cloudflare": 2 },
    "soa": "elliott.ns.cloudflare.com serial=2413856909"
  },
  "results": [
    {
      "host": "www.example.com",
      "ips": ["104.20.23.154", "172.66.147.243"],
      "tags": ["cdn:cloudflare", "waf:cloudflare"],
      "asn": { "asn": "13335", "org": "CLOUDFLARENET - Cloudflare, Inc., US" },
      "http": { "status": 200, "title": "Example Domain", "tech": ["Cloudflare"] },
      "tls": {
        "sans": ["example.com"],
        "issuer": "SSL Corporation / Cloudflare TLS Issuing ECC CA 3",
        "not_after": "2026-10-27",
        "expired": false
      }
    }
  ]
}

Favicon hashing uses signed MurmurHash3 x86-32 over base64.encodebytes, the same construction Shodan documents for favicon search.


Config

python3 -m subenum example.com -c subenum.json
{
  "resolvers": ["1.1.1.1", "8.8.8.8", "9.9.9.9"],
  "concurrency": 80,
  "rate": 40,
  "timeout": 8,
  "ports": "80,443,8080,8443"
}

Design notes

  • Resolvers are validated before brute force: a known name must resolve, a .invalid name must not. Liars are dropped.
  • Wildcard filtering probes several random labels. Hosts whose A/AAAA set is a subset of the wildcard set are discarded unless they have a distinct CNAME.
  • AXFR is attempted in parallel against a few authoritative NS with a short timeout. Most modern zones refuse; an open transfer is still worth knowing about.
  • NSEC walking talks to the zone’s nameserver with recursion disabled. Covering proofs from a public recursive resolver are not treated as a zone listing. NSEC3 hashed names are reported, not cracked.
  • CNAME providers (GitHub Pages, Heroku, S3, Fastly, …) are inventory tags, not takeover instructions.
  • Rate limiting (--rate) is a token bucket on DNS so you do not burn shared resolvers.

Disclaimer

Educational use and authorized testing only (your assets, a written pentest SoW, or an in-scope bug bounty). Unauthorized scanning is typically a legal and terms-of-service violation. You are responsible for staying inside the law and the program rules. The authors are not responsible for misuse.


License

Apache License 2.0. Derived from alexbieber/subenum.

External tools remain under their own licenses; subenum only execs them when you have installed them.

About

This tool is a subdomain enumerator . Thanks for using!. ;)

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages