ENUM^:Enumeration

Amass

OWASP Amass v5 subdomain enumeration and attack-surface mapping — enum, ASN/CIDR discovery, and reading results back out of its graph database.

intermediateupdated 2026-09-25Amass · subfinder · assetfinder · puredns

Amass

OWASP Amass performs passive-by-default subdomain enumeration and attack-surface mapping, combining certificate transparency logs, passive DNS sources, search engines, and (optionally) active techniques like zone-transfer attempts and DNS brute-forcing. It’s typically the first name-gathering pass alongside subfinder/assetfinder in Stage 00 — Passive External Recon.

[!warning] Version note — v5 changed the CLI shape Amass v5 rewrote storage around a local graph database (the “OAM” — Open Asset Model) served by a background engine process. Older tutorials referencing amass intel -asn, amass intel -whois, or amass enum -o results.txt are describing v3/v4 and do not apply to v5 — there is no intel subcommand anymore; ASN/CIDR discovery moved directly into enum, and results live in the graph DB, read back out with amass subs rather than a plain output file. Confirmed against v5.1.1 (amass -version).

Subcommands (v5)

amass enum    interface with the engine that performs enumerations
amass subs    analyze and present discovered subdomains and associated data
amass track   diff OAM data to identify newly discovered assets since a timestamp
amass viz     generate graph visualizations (DOT / GEXF) from OAM data
amass assoc   query the OAM along an association "triple" walk
amass engine  run/manage the collection engine that backs the OAM database

amass enum auto-starts the background engine the first time it’s needed (ps aux | grep amass will show amass engine running afterward); it stays up across invocations so repeated enum/subs calls against the same domain reuse the same graph DB. Stop it with pkill -f "amass engine" if you want a clean slate.

amass enum — the main pass

# passive is the DEFAULT in v5 — this alone is the safe, non-contact baseline
amass enum -d $DOMAIN

# multiple domains in one run
amass enum -d $DOMAIN,$DOMAIN2

# active mode: adds zone-transfer attempts + cert-name grabs against the target's own infra
amass enum -d $DOMAIN -active

# brute force on top of the passive/active sources, with a custom wordlist
amass enum -d $DOMAIN -brute -w /path/to/subdomains.txt

# ASN/CIDR-driven discovery — folded directly into `enum` in v5 (no more `intel` subcommand)
amass enum -asn 12345
amass enum -cidr 203.0.113.0/24 -d $DOMAIN

# seed the engine with names you already have (other tools' output) so they're in scope from the start
amass enum -d $DOMAIN -nf known_subs.txt

# write files out under a directory instead of just the graph DB
amass enum -d $DOMAIN -oA ./recon/amass_$DOMAIN
FlagDescriptionDefault
-d valueDomain(s), comma-separated—
-df fileFile of domains—
-activeAttempt zone transfers + cert-name grabs (this is what makes a run “active”, not a -passive flag — that flag still exists but is a no-op since passive is now default)off
-bruteRun DNS brute forcing after the search/API sourcesoff
-w file / -aw fileWordlist for brute forcing / name alterations—
-altsGenerate altered/permuted names from what’s foundoff
-asn valueASN(s), comma-separated — org-wide discovery—
-cidr valueCIDR(s), comma-separated—
-addr valueIPs/ranges (192.168.1.1-254)—
-p valuePorts to check when resolving80, 443
-r / -tr valueUntrusted / trusted DNS resolver IPssystem default
-nf fileSeed the engine with already-known names (from other tools)—
-bl value / -blf fileBlacklist subdomains (inline / from file)—
-timeout NMinutes with no progress before terminating30
-oA prefixPath prefix for all output files—
-dir pathDirectory holding the graph databasedefault state dir
-list(dead in v5.1.1 — does not print sources; read datasources.yaml instead)—
-include / -exclude valueRestrict to / drop specific data sourcesall
-vVerbose/debug outputoff
-silentNo output during the runoff

[!tools] Related in the same recon stage subfinder, assetfinder, and puredns (wildcard-aware resolving) — see Stage 00 for unioning all of them together. No single passive source is complete; run at least two and merge.

Config files (v5) — wiring API keys

v5 reads two YAML files from the state dir — config.yaml (scope, engine/DB, brute/alt toggles, per-asset transform TTLs) and datasources.yaml (per-source credentials). Point any subcommand at them with -config.

Linux:  ~/.config/amass/{config.yaml,datasources.yaml}
macOS:  ~/Library/Application Support/amass/{config.yaml,datasources.yaml}
# config.yaml  (trimmed to the parts you actually touch)
scope:
  domains:
    - example.com              # root/registered domains in scope
  # ips:   [ 192.0.2.1, 192.168.0.3-8 ]
  # cidrs: [ 192.0.2.0/24 ]
  ports: [ 80, 443, 8080, 8443 ]   # ports used when probing services
  # blacklist:
  #   - dev.example.com
options:
  datasources: "./datasources.yaml"   # path is relative to THIS file, not your cwd
  # engine:   "http://127.0.0.1:4000"           # reuse/point at a specific engine
  # database: "postgres://amass:amass4OWASP@assetdb:5432/assetdb"  # or bolt://...  (neo4j)
  bruteforce:
    enabled: false
    wordlists: [ "./wordlists/short-wordlist.txt" ]
  alterations:
    enabled: false
  default_transform_values:
    ttl: 1440          # minutes a result is cached before re-querying
    confidence: 50
    priority: 5
transformations:       # which asset->source expansions run, and their TTLs
  FQDN->ALL:
  FQDN->DomainRecord:
    ttl: 43200
  TLSCertificate->ALL:
    ttl: 10800
# datasources.yaml  — uncomment a source and drop your key in
global_options:
  minimum_ttl: 1440                 # floor applied when a source omits its own ttl
datasources:
  - name: VirusTotal
    ttl: 10080
    creds:
      account:
        apikey: VT_API_KEY_HERE
  - name: SecurityTrails
    ttl: 1440
    creds:
      account:
        apikey: ST_API_KEY_HERE
  - name: Shodan
    ttl: 10080
    creds:
      account:
        apikey: SHODAN_API_KEY_HERE
  - name: Chaos                     # ProjectDiscovery — same key subfinder uses
    ttl: 4320
    creds:
      account:
        apikey: CHAOS_API_KEY_HERE

Some sources key differently — a named account, multiple accounts, or username+secret:

  - name: GitHub                    # keyed by a custom account name, not "account"
    ttl: 4320
    creds:
      accountname:
        apikey: ghp_xxx
  - name: PassiveTotal              # username + apikey (CIRCL/FOFA/Yandex/ZoomEye are similar)
    creds:
      account:
        username: you@example.com
        apikey: PT_KEY
  - name: C99                       # multiple keys under account1/account2
    ttl: 4320
    creds:
      account1: { apikey: KEY1 }
      account2: { apikey: KEY2 }
amass enum -config ./config.yaml -d example.com          # enum uses the configured keys
amass enum -d example.com                                # uses the default-path config if present
amass subs -config ./config.yaml -d example.com -names   # subs/track/assoc/viz also accept -config

[!tip] The datasources path is resolved relative to config.yaml, not your shell’s cwd. Keep config.yaml, datasources.yaml, and wordlists/ in the same directory, or use absolute paths.

[!info] The default OAM store is a local SQLite graph — asset.db (plus asset.db-wal/asset.db-shm) in the state dir. For a shared/team graph, set options.database to a Postgres (postgres://...) or Neo4j (bolt://...) URL instead.

[!warning] Only VirusTotal, SecurityTrails, Shodan (and Chaos) are wired above because those are what v5.1.1 actually ships in datasources.yaml. Censys is not a v5.1.1 data source — there is no stanza for it; don’t invent one. The shipped roster (VirusTotal, SecurityTrails, Shodan, Chaos, BinaryEdge, FullHunt, Netlas, LeakIX, IntelX, ZoomEye, AlienVault, HackerTarget, URLScan, WhoisXMLAPI, GitHub/GitLab, PassiveTotal, and more) is exactly the set of - name: entries in that file.

Trusted vs untrusted resolvers

# untrusted resolvers (bulk, for volume) from a file; trusted (reliable) inline
amass enum -d example.com -rf untrusted-resolvers.txt -tr 1.1.1.1,8.8.8.8,9.9.9.9

# a few untrusted resolvers inline instead of a file
amass enum -d example.com -r 1.0.0.1,8.8.4.4
FlagClassInputUse for
-r valueuntrustedinline IPs, comma-sep, repeatablebulk resolvers, throughput
-rf fileuntrustedfile of resolver IPsa large scraped public-resolver list
-tr valuetrustedinline IPs, comma-sep, repeatablereliable resolvers used to validate findings

[!note] v5.1.1 has no trusted-resolver file flag — trusted resolvers are inline-only via -tr. Only the untrusted pool takes a file (-rf). Keep -tr a short list you actually trust (1.1.1.1 / 8.8.8.8 / 9.9.9.9); dump the big list into -rf. Amass validates candidate names against the trusted set to cut false positives from unreliable untrusted resolvers.

Reading results back out

Amass v5 doesn’t dump to stdout the way older versions did — query the graph DB with subs after enum finishes:

amass subs -d $DOMAIN -names            # just the discovered names, one per line
amass subs -d $DOMAIN -ip               # names + IPs
amass subs -d $DOMAIN -ipv4             # IPv4 only
amass subs -d $DOMAIN -summary          # ASN table summary instead of names
amass subs -d $DOMAIN -names > subs_amass.txt   # pipe into the union step alongside subfinder/assetfinder

More subs reads

amass subs -d example.com -ipv6            # IPv6 addresses only
amass subs -d example.com -show            # full result set for the enumeration index + domains
amass subs -df roots.txt -names            # read many root domains from a file
amass subs -dir ./engagement-db -d example.com -names   # read a NON-default graph dir
amass subs -d example.com -names -o subs.txt            # also tee terminal output to a file
FlagOutput
-namesdiscovered FQDNs, one per line
-ip / -ipv4 / -ipv6names with resolved addresses (all / v4 / v6)
-summaryASN + netblock rollup instead of names
-showfull results for the enumeration index + provided domains
-df fileread root domains from a file
-dir pathquery a specific graph-DB directory (not the default state dir)
-o filetee stdout/stderr to a text file
-config fileapply a YAML config while reading

amass assoc — association walks

[!info] amass assoc walks associations in the OAM graph — pivoting from a seed asset (domain, org, netblock, ASN, TLS cert…) to other assets the graph has linked to it. It is how v5 replaces the old intel “find related orgs/domains” idea: enumerate first with enum, then walk the graph you built.

# association walk over the default graph DB (one hop per -tN)
amass assoc -t1 '<subject> <predicate> <object>'

# chain up to ten hops (-t1 .. -t10), or load the triples from a file
amass assoc -tf triples.txt

# walk a specific (non-default) graph DB, or apply a config
amass assoc -dir ./engagement-db -t1 '<triple>'
amass assoc -config ./config.yaml -tf triples.txt
FlagMeaning
-t1 … -t10up to 10 triples defining the walk (one hop each)
-tf filefile containing the triples list
-dir pathgraph-DB directory to query
-config fileYAML config to use

[!warning] The exact triple grammar (asset-type / predicate / asset-type ordering and quoting) is version-specific and easy to get wrong — confirm the fields against amass assoc -h and the Open Asset Model docs before scripting a walk. Don’t guess predicate names. OAM asset types you’ll pivot between include FQDN, IPAddress, Netblock, AutonomousSystem, TLSCertificate, DomainRecord, and Organization.

Diffing over time & visualizing

# only assets discovered since a given time — good for re-scanning a target periodically
amass track -d $DOMAIN -since '09/01 00:00:00 2026 UTC'

# export the asset graph — viz REQUIRES -d (it exits "No root domain names were
# provided" before it even reads the DB if you give only -dir)
amass viz -d $DOMAIN -dir <graph-db-dir> -dot  -oA ./recon/amass_graph
amass viz -d $DOMAIN -dir <graph-db-dir> -gexf -oA ./recon/amass_graph   # open the .gexf in Gephi

[!warning] Watch out

  • -active sends zone-transfer attempts and TLS connections straight at the target’s own nameservers/hosts — that’s active recon, not passive; keep engagements scoped accordingly (same rule as Stage 00’s amass enum -passive note, now spelled -active’s absence rather than a -passive flag’s presence).
  • API keys for VirusTotal/SecurityTrails/Shodan/Netlas/FullHunt etc. meaningfully increase yield — configure them in datasources.yaml and pass it with -config rather than relying on the free/keyless sources alone. There is no working “list sources” flag in v5.1.1 (-list is dead code — it neither lists nor errors usefully); the authoritative roster is the set of - name: entries in the shipped datasources.yaml (see above). Censys is not a v5.1.1 source — don’t add a stanza for it.
  • The background engine persists between runs; if a subs/track query looks stale, confirm you’re pointed at the same -dir the enum run used, or that the engine process is even still the one you expect (ps aux | grep amass).

amass track — periodic re-scan diffing

track reads the same OAM graph enum writes and prints assets newer than a timestamp — good for scheduled re-scans where you only want the delta.

# names added since a timestamp (Go reference layout: 01/02 15:04:05 2006 MST)
amass track -d example.com -since '09/01 00:00:00 2026 UTC'

# many roots from a file, against a specific graph DB
amass track -df roots.txt -dir ./engagement-db -since '09/01 00:00:00 2026 UTC'

Weekly re-scan capturing only what’s new since last run:

amass enum -d example.com          # refresh the graph first (same -dir)
# macOS date:
amass track -d example.com -since "$(date -u -v-7d '+%m/%d %H:%M:%S %Y UTC')"
# Linux date:
# amass track -d example.com -since "$(date -u -d '7 days ago' '+%m/%d %H:%M:%S %Y UTC')"

[!tip] track only reports — it never enumerates. Run a fresh enum against the same -dir first, then track -since <last run> for the delta. If the graph isn’t persisted between runs, the diff is meaningless. The -since string must match Go’s reference layout 01/02 15:04:05 2006 MST (month/day, then year, then zone).

End-to-end recon pipeline (union + resolve)

No single passive source is complete. Union amass with subfinder + assetfinder, then resolve the merged list wildcard-aware with puredns.

DOMAIN=example.com
UNTRUSTED=untrusted-resolvers.txt     # big scraped public-resolver list

# 1) three passive engines (union beats any one source)
amass enum -d "$DOMAIN" -tr 1.1.1.1,8.8.8.8,9.9.9.9 -rf "$UNTRUSTED"
subfinder -d "$DOMAIN" -all -silent -o subfinder.txt
assetfinder --subs-only "$DOMAIN" > assetfinder.txt

# 2) pull amass names back out of the OAM graph
amass subs -d "$DOMAIN" -names > amass.txt

# 3) union + dedupe
cat amass.txt subfinder.txt assetfinder.txt | sort -u > all_subs.txt

# 4) wildcard-aware resolve — keep only names that actually resolve
puredns resolve all_subs.txt \
  --resolvers "$UNTRUSTED" \
  --resolvers-trusted trusted-resolvers.txt \
  -w resolved.txt

# 5) (optional) feed the union back to amass as seeds and re-run for depth
amass enum -d "$DOMAIN" -nf all_subs.txt
amass subs -d "$DOMAIN" -names > amass_round2.txt

[!tools] Pipeline pieces subfinder -all (every source), assetfinder --subs-only, puredns (wildcard-aware mass resolver). The full unioning workflow lives in Stage 00 — Passive External Recon. Feeding the merged list back via -nf lets amass mine the graph around names the other tools found.

Common v5 errors

SymptomCauseFix
The Amass engine is already running.a background amass engine still holds :4000 from a prior runreuse it (usually fine), or pkill -f "amass engine" for a clean slate, or target another with -engine http://127.0.0.1:PORT
Configuration error: No root domain names were providedenum/subs/track have no scopepass -d example.com (or -df roots.txt), or set scope.domains in config.yaml
subs/track return stale or empty dataquerying a different -dir than enum wrote topoint -dir at the same directory enum used; defaults are ~/.config/amass/ (Linux) / ~/Library/Application Support/amass/ (macOS)
enum finishes with almost no namesonly keyless sources are activewire API keys in datasources.yaml (-config); add -brute -w <wordlist> and/or -active if scope allows
need a genuinely clean runthe OAM SQLite graph persists between runsdelete asset.db asset.db-shm asset.db-wal from the state dir, or use a fresh -dir ./new-db

[!warning] The graph DB is a real on-disk SQLite file — asset.db plus asset.db-wal/asset.db-shm — in the state dir, alongside per-run session-<uuid>/ dirs and amass_engine_<timestamp>.log. When results look wrong, read the newest engine log and confirm which -dir you’re actually hitting before blaming the sources.

See Also

Sources

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.