amass.md (19320B)
1 --- 2 title: "Amass" 3 description: "OWASP Amass v5 subdomain enumeration and attack-surface mapping — enum, ASN/CIDR discovery, and reading results back out of its graph database." 4 category: enumeration 5 tags: [enumeration, osint, recon, dns, subdomains, attack-surface, api-keys] 6 tools: [Amass, subfinder, assetfinder, puredns] 7 difficulty: intermediate 8 updated: "2026-09-25" 9 --- 10 11 # Amass 12 13 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](/sheets/pentest-workflow/passive-external-recon). 14 15 > [!warning] Version note — v5 changed the CLI shape 16 > 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`). 17 18 ## Subcommands (v5) 19 20 ```text 21 amass enum interface with the engine that performs enumerations 22 amass subs analyze and present discovered subdomains and associated data 23 amass track diff OAM data to identify newly discovered assets since a timestamp 24 amass viz generate graph visualizations (DOT / GEXF) from OAM data 25 amass assoc query the OAM along an association "triple" walk 26 amass engine run/manage the collection engine that backs the OAM database 27 ``` 28 29 `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. 30 31 ## `amass enum` — the main pass 32 33 ```bash 34 # passive is the DEFAULT in v5 — this alone is the safe, non-contact baseline 35 amass enum -d $DOMAIN 36 37 # multiple domains in one run 38 amass enum -d $DOMAIN,$DOMAIN2 39 40 # active mode: adds zone-transfer attempts + cert-name grabs against the target's own infra 41 amass enum -d $DOMAIN -active 42 43 # brute force on top of the passive/active sources, with a custom wordlist 44 amass enum -d $DOMAIN -brute -w /path/to/subdomains.txt 45 46 # ASN/CIDR-driven discovery — folded directly into `enum` in v5 (no more `intel` subcommand) 47 amass enum -asn 12345 48 amass enum -cidr 203.0.113.0/24 -d $DOMAIN 49 50 # seed the engine with names you already have (other tools' output) so they're in scope from the start 51 amass enum -d $DOMAIN -nf known_subs.txt 52 53 # write files out under a directory instead of just the graph DB 54 amass enum -d $DOMAIN -oA ./recon/amass_$DOMAIN 55 ``` 56 57 | Flag | Description | Default | 58 |---|---|---| 59 | `-d value` | Domain(s), comma-separated | — | 60 | `-df file` | File of domains | — | 61 | `-active` | Attempt 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 | 62 | `-brute` | Run DNS brute forcing after the search/API sources | off | 63 | `-w file` / `-aw file` | Wordlist for brute forcing / name alterations | — | 64 | `-alts` | Generate altered/permuted names from what's found | off | 65 | `-asn value` | ASN(s), comma-separated — org-wide discovery | — | 66 | `-cidr value` | CIDR(s), comma-separated | — | 67 | `-addr value` | IPs/ranges (`192.168.1.1-254`) | — | 68 | `-p value` | Ports to check when resolving | 80, 443 | 69 | `-r` / `-tr value` | Untrusted / trusted DNS resolver IPs | system default | 70 | `-nf file` | Seed the engine with already-known names (from other tools) | — | 71 | `-bl value` / `-blf file` | Blacklist subdomains (inline / from file) | — | 72 | `-timeout N` | Minutes with no progress before terminating | 30 | 73 | `-oA prefix` | Path prefix for all output files | — | 74 | `-dir path` | Directory holding the graph database | default state dir | 75 | `-list` | *(dead in v5.1.1 — does not print sources; read `datasources.yaml` instead)* | — | 76 | `-include` / `-exclude value` | Restrict to / drop specific data sources | all | 77 | `-v` | Verbose/debug output | off | 78 | `-silent` | No output during the run | off | 79 80 > [!tools] Related in the same recon stage 81 > [subfinder](https://github.com/projectdiscovery/subfinder), [assetfinder](https://github.com/tomnomnom/assetfinder), and [puredns](https://github.com/d3mondev/puredns) (wildcard-aware resolving) — see [Stage 00](/sheets/pentest-workflow/passive-external-recon) for unioning all of them together. No single passive source is complete; run at least two and merge. 82 83 ## Config files (v5) — wiring API keys 84 85 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`. 86 87 ```text 88 Linux: ~/.config/amass/{config.yaml,datasources.yaml} 89 macOS: ~/Library/Application Support/amass/{config.yaml,datasources.yaml} 90 ``` 91 92 ```yaml 93 # config.yaml (trimmed to the parts you actually touch) 94 scope: 95 domains: 96 - example.com # root/registered domains in scope 97 # ips: [ 192.0.2.1, 192.168.0.3-8 ] 98 # cidrs: [ 192.0.2.0/24 ] 99 ports: [ 80, 443, 8080, 8443 ] # ports used when probing services 100 # blacklist: 101 # - dev.example.com 102 options: 103 datasources: "./datasources.yaml" # path is relative to THIS file, not your cwd 104 # engine: "http://127.0.0.1:4000" # reuse/point at a specific engine 105 # database: "postgres://amass:amass4OWASP@assetdb:5432/assetdb" # or bolt://... (neo4j) 106 bruteforce: 107 enabled: false 108 wordlists: [ "./wordlists/short-wordlist.txt" ] 109 alterations: 110 enabled: false 111 default_transform_values: 112 ttl: 1440 # minutes a result is cached before re-querying 113 confidence: 50 114 priority: 5 115 transformations: # which asset->source expansions run, and their TTLs 116 FQDN->ALL: 117 FQDN->DomainRecord: 118 ttl: 43200 119 TLSCertificate->ALL: 120 ttl: 10800 121 ``` 122 123 ```yaml 124 # datasources.yaml — uncomment a source and drop your key in 125 global_options: 126 minimum_ttl: 1440 # floor applied when a source omits its own ttl 127 datasources: 128 - name: VirusTotal 129 ttl: 10080 130 creds: 131 account: 132 apikey: VT_API_KEY_HERE 133 - name: SecurityTrails 134 ttl: 1440 135 creds: 136 account: 137 apikey: ST_API_KEY_HERE 138 - name: Shodan 139 ttl: 10080 140 creds: 141 account: 142 apikey: SHODAN_API_KEY_HERE 143 - name: Chaos # ProjectDiscovery — same key subfinder uses 144 ttl: 4320 145 creds: 146 account: 147 apikey: CHAOS_API_KEY_HERE 148 ``` 149 150 Some sources key differently — a named account, multiple accounts, or username+secret: 151 152 ```yaml 153 - name: GitHub # keyed by a custom account name, not "account" 154 ttl: 4320 155 creds: 156 accountname: 157 apikey: ghp_xxx 158 - name: PassiveTotal # username + apikey (CIRCL/FOFA/Yandex/ZoomEye are similar) 159 creds: 160 account: 161 username: you@example.com 162 apikey: PT_KEY 163 - name: C99 # multiple keys under account1/account2 164 ttl: 4320 165 creds: 166 account1: { apikey: KEY1 } 167 account2: { apikey: KEY2 } 168 ``` 169 170 ```bash 171 amass enum -config ./config.yaml -d example.com # enum uses the configured keys 172 amass enum -d example.com # uses the default-path config if present 173 amass subs -config ./config.yaml -d example.com -names # subs/track/assoc/viz also accept -config 174 ``` 175 176 > [!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. 177 178 > [!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. 179 180 > [!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. 181 182 ## Trusted vs untrusted resolvers 183 184 ```bash 185 # untrusted resolvers (bulk, for volume) from a file; trusted (reliable) inline 186 amass enum -d example.com -rf untrusted-resolvers.txt -tr 1.1.1.1,8.8.8.8,9.9.9.9 187 188 # a few untrusted resolvers inline instead of a file 189 amass enum -d example.com -r 1.0.0.1,8.8.4.4 190 ``` 191 192 | Flag | Class | Input | Use for | 193 |---|---|---|---| 194 | `-r value` | untrusted | inline IPs, comma-sep, repeatable | bulk resolvers, throughput | 195 | `-rf file` | untrusted | file of resolver IPs | a large scraped public-resolver list | 196 | `-tr value` | trusted | inline IPs, comma-sep, repeatable | reliable resolvers used to validate findings | 197 198 > [!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. 199 200 ## Reading results back out 201 202 Amass v5 doesn't dump to stdout the way older versions did — query the graph DB with `subs` after `enum` finishes: 203 204 ```bash 205 amass subs -d $DOMAIN -names # just the discovered names, one per line 206 amass subs -d $DOMAIN -ip # names + IPs 207 amass subs -d $DOMAIN -ipv4 # IPv4 only 208 amass subs -d $DOMAIN -summary # ASN table summary instead of names 209 amass subs -d $DOMAIN -names > subs_amass.txt # pipe into the union step alongside subfinder/assetfinder 210 ``` 211 212 ## More `subs` reads 213 214 ```bash 215 amass subs -d example.com -ipv6 # IPv6 addresses only 216 amass subs -d example.com -show # full result set for the enumeration index + domains 217 amass subs -df roots.txt -names # read many root domains from a file 218 amass subs -dir ./engagement-db -d example.com -names # read a NON-default graph dir 219 amass subs -d example.com -names -o subs.txt # also tee terminal output to a file 220 ``` 221 222 | Flag | Output | 223 |---|---| 224 | `-names` | discovered FQDNs, one per line | 225 | `-ip` / `-ipv4` / `-ipv6` | names with resolved addresses (all / v4 / v6) | 226 | `-summary` | ASN + netblock rollup instead of names | 227 | `-show` | full results for the enumeration index + provided domains | 228 | `-df file` | read root domains from a file | 229 | `-dir path` | query a specific graph-DB directory (not the default state dir) | 230 | `-o file` | tee stdout/stderr to a text file | 231 | `-config file` | apply a YAML config while reading | 232 233 ## `amass assoc` — association walks 234 235 > [!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. 236 237 ```bash 238 # association walk over the default graph DB (one hop per -tN) 239 amass assoc -t1 '<subject> <predicate> <object>' 240 241 # chain up to ten hops (-t1 .. -t10), or load the triples from a file 242 amass assoc -tf triples.txt 243 244 # walk a specific (non-default) graph DB, or apply a config 245 amass assoc -dir ./engagement-db -t1 '<triple>' 246 amass assoc -config ./config.yaml -tf triples.txt 247 ``` 248 249 | Flag | Meaning | 250 |---|---| 251 | `-t1` … `-t10` | up to 10 triples defining the walk (one hop each) | 252 | `-tf file` | file containing the triples list | 253 | `-dir path` | graph-DB directory to query | 254 | `-config file` | YAML config to use | 255 256 > [!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`. 257 258 ## Diffing over time & visualizing 259 260 ```bash 261 # only assets discovered since a given time — good for re-scanning a target periodically 262 amass track -d $DOMAIN -since '09/01 00:00:00 2026 UTC' 263 264 # export the asset graph — viz REQUIRES -d (it exits "No root domain names were 265 # provided" before it even reads the DB if you give only -dir) 266 amass viz -d $DOMAIN -dir <graph-db-dir> -dot -oA ./recon/amass_graph 267 amass viz -d $DOMAIN -dir <graph-db-dir> -gexf -oA ./recon/amass_graph # open the .gexf in Gephi 268 ``` 269 270 > [!warning] Watch out 271 > - `-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](/sheets/pentest-workflow/passive-external-recon), now spelled `-active`'s absence rather than a `-passive` flag's presence). 272 > - 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. 273 > - 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`). 274 275 ## `amass track` — periodic re-scan diffing 276 277 `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. 278 279 ```bash 280 # names added since a timestamp (Go reference layout: 01/02 15:04:05 2006 MST) 281 amass track -d example.com -since '09/01 00:00:00 2026 UTC' 282 283 # many roots from a file, against a specific graph DB 284 amass track -df roots.txt -dir ./engagement-db -since '09/01 00:00:00 2026 UTC' 285 ``` 286 287 Weekly re-scan capturing only what's new since last run: 288 289 ```bash 290 amass enum -d example.com # refresh the graph first (same -dir) 291 # macOS date: 292 amass track -d example.com -since "$(date -u -v-7d '+%m/%d %H:%M:%S %Y UTC')" 293 # Linux date: 294 # amass track -d example.com -since "$(date -u -d '7 days ago' '+%m/%d %H:%M:%S %Y UTC')" 295 ``` 296 297 > [!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). 298 299 ## End-to-end recon pipeline (union + resolve) 300 301 No single passive source is complete. Union amass with subfinder + assetfinder, then resolve the merged list wildcard-aware with puredns. 302 303 ```bash 304 DOMAIN=example.com 305 UNTRUSTED=untrusted-resolvers.txt # big scraped public-resolver list 306 307 # 1) three passive engines (union beats any one source) 308 amass enum -d "$DOMAIN" -tr 1.1.1.1,8.8.8.8,9.9.9.9 -rf "$UNTRUSTED" 309 subfinder -d "$DOMAIN" -all -silent -o subfinder.txt 310 assetfinder --subs-only "$DOMAIN" > assetfinder.txt 311 312 # 2) pull amass names back out of the OAM graph 313 amass subs -d "$DOMAIN" -names > amass.txt 314 315 # 3) union + dedupe 316 cat amass.txt subfinder.txt assetfinder.txt | sort -u > all_subs.txt 317 318 # 4) wildcard-aware resolve — keep only names that actually resolve 319 puredns resolve all_subs.txt \ 320 --resolvers "$UNTRUSTED" \ 321 --resolvers-trusted trusted-resolvers.txt \ 322 -w resolved.txt 323 324 # 5) (optional) feed the union back to amass as seeds and re-run for depth 325 amass enum -d "$DOMAIN" -nf all_subs.txt 326 amass subs -d "$DOMAIN" -names > amass_round2.txt 327 ``` 328 329 > [!tools] Pipeline pieces 330 > [subfinder](https://github.com/projectdiscovery/subfinder) `-all` (every source), [assetfinder](https://github.com/tomnomnom/assetfinder) `--subs-only`, [puredns](https://github.com/d3mondev/puredns) (wildcard-aware mass resolver). The full unioning workflow lives in [Stage 00 — Passive External Recon](/sheets/pentest-workflow/passive-external-recon). Feeding the merged list back via `-nf` lets amass mine the graph around names the other tools found. 331 332 ## Common v5 errors 333 334 | Symptom | Cause | Fix | 335 |---|---|---| 336 | `The Amass engine is already running.` | a background `amass engine` still holds `:4000` from a prior run | reuse it (usually fine), or `pkill -f "amass engine"` for a clean slate, or target another with `-engine http://127.0.0.1:PORT` | 337 | `Configuration error: No root domain names were provided` | `enum`/`subs`/`track` have no scope | pass `-d example.com` (or `-df roots.txt`), or set `scope.domains` in `config.yaml` | 338 | `subs`/`track` return stale or empty data | querying a different `-dir` than `enum` wrote to | point `-dir` at the same directory `enum` used; defaults are `~/.config/amass/` (Linux) / `~/Library/Application Support/amass/` (macOS) | 339 | enum finishes with almost no names | only keyless sources are active | wire API keys in `datasources.yaml` (`-config`); add `-brute -w <wordlist>` and/or `-active` if scope allows | 340 | need a genuinely clean run | the OAM SQLite graph persists between runs | delete `asset.db asset.db-shm asset.db-wal` from the state dir, or use a fresh `-dir ./new-db` | 341 342 > [!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. 343 344 ## See Also 345 346 - **[Passive External Recon](/sheets/pentest-workflow/passive-external-recon)** — where subdomain enumeration sits in the wider OSINT / attack-surface stage. 347 - **[Recon & Host Discovery](/sheets/pentest-workflow/recon-and-host-discovery)** — turning resolved names into a live-host list to scan. 348 - **[Nmap](/sheets/enumeration/nmap)** — port-scan the hosts `amass` resolves; feed its output with `-iL`. 349 - **[Web Enumeration & Exploitation](/sheets/pentest-workflow/web-enumeration-and-exploitation)** — vhost and content discovery once you have the resolved web surface. 350 351 ## Sources 352 353 - https://github.com/owasp-amass/amass 354 - `amass -h`, `amass enum -h`, `amass subs -h`, `amass track -h`, `amass viz -h` (v5.1.1, confirmed locally)