The first time I ran OWASP Amass against a real target, the sheer volume of subdomains it surfaced from passive sources alone made me realize how much attack surface most organizations don’t even know they have. Amass isn’t just a subdomain finder — it’s a full attack surface mapping framework that combines dozens of passive intelligence sources with active DNS techniques to build a genuine picture of an organization’s external footprint. In this guide, I’ll walk through everything from installation to the internal architecture, using only commands and outputs I’ve actually run and verified myself.
What OWASP Amass Is
OWASP Amass is an open-source Go application maintained under the OWASP Foundation umbrella, built for in-depth attack surface mapping and external asset discovery. It performs DNS enumeration, network mapping, and reconnaissance by pulling from nearly a hundred different external data sources, then correlating everything into a coherent picture of a target organization’s infrastructure — domains, subdomains, IP ranges, ASNs, and the relationships between them.
I want to be precise about versioning here because it matters for anyone following tutorials online: as of testing for this article, the project’s master branch on GitHub has moved to module path v5, while the latest stable tagged release most people actually install and run is v4.2.0. If you see conflicting command syntax across different tutorials, this version difference is almost always why.
Architecture and Internal Working
Amass is built around two primary subcommands, and understanding the difference between them is the single most important conceptual thing to get right.
amass intel is the target discovery phase. It’s used before you know your full scope — for example, if you only know an organization’s name or a single seed domain and want to discover related domains, ASNs, and netblocks that belong to the same organization. It supports reverse WHOIS lookups, organization name searches against ASN registration data, and can enumerate IP ranges tied to a given ASN.
amass enum is the actual enumeration and network mapping phase, run once you have a defined scope of root domains. This is where the real subdomain discovery happens, using two complementary strategies:
- Passive collection — querying dozens of third-party data sources (certificate transparency logs, search engine scraping, public datasets, threat intelligence APIs) without ever touching the target’s own infrastructure. This is the default mode.
- Active techniques (opt-in via
-activeor-brute) — DNS brute-forcing with wordlists, zone transfer attempts, certificate name grabbing directly from target hosts, and recursive brute-forcing on newly discovered subdomains.
Internally, Amass correlates results using a graph-based data model — hostnames, IP addresses, ASNs, and netblocks are all nodes with typed relationships between them (resolves_to, contains, announces, etc.). This graph approach is what lets Amass answer questions like “show me every subdomain that eventually resolves into this specific ASN” rather than just producing a flat list of names.
I confirmed the exact subcommand structure by running the binary directly:
Usage: amass intel|enum [options]
-h Show the program usage message
-help
Show the program usage message
-version
Print the version number of this Amass binary
Subcommands:
amass intel - Discover targets for enumerations
amass enum - Perform enumerations and network mapping
Note: in v4.2.0, intel and enum are the only two real subcommands. Older tutorials referencing amass db, amass viz, or amass track are describing an earlier version’s command set — running those against a current v4 binary just falls back to the top-level help text, which I verified directly rather than assuming.
Installation
Amass is written in Go, and there are three realistic ways to get it running.
Method 1: Precompiled release binary (fastest, and the method I verified end-to-end)
curl -sL -o amass.zip \
"https://github.com/owasp-amass/amass/releases/download/v4.2.0/amass_Linux_amd64.zip"
unzip -o amass.zip -d amass_extracted
cd amass_extracted/amass_Linux_amd64
chmod +x amass
./amass -version
Actual verified output:
v4.2.0
This is the method I recommend for most people — no Go toolchain required, no dependency resolution headaches, just a working binary in seconds.
Method 2: Build from source with go install
go install -v github.com/owasp-amass/amass/v4/...@master
I want to flag something important I discovered while testing this exact command: pulling @master currently resolves to the v5 module path, not v4, because the module declaration in go.mod on the master branch has already been bumped:
module github.com/owasp-amass/amass/v5
go 1.26.0
If you specifically want the stable v4 line, pin an explicit tag instead:
go install -v github.com/owasp-amass/amass/v4/...@v4.2.0
Also be aware that building from source pulls in a fairly wide dependency tree — I hit real, reproducible failures in a network-restricted sandbox because the build needs to reach golang.org/x/..., google.golang.org/protobuf, go.uber.org/ratelimit, and a few other non-GitHub Go module hosts in addition to GitHub itself:
../go/pkg/mod/github.com/miekg/dns@v1.1.55/udp.go:9:2: unrecognized import path "golang.org/x/net":
reading https://golang.org/x/net?go-get=1: 403 Forbidden
server response: Host not in allowlist: golang.org.
If you’re building inside a locked-down CI runner or an air-gapped build environment, make sure golang.org, google.golang.org, go.uber.org, layeh.com, and proxy.golang.org are reachable, or use the precompiled binary method instead.
Method 3: Docker
docker run -v ~/amass_out:/output owaspamass/amass enum -d example.com -o /output
This is the cleanest option if you don’t want Go on your host at all and don’t mind the image size.
Method 4: Package managers
# Kali Linux / Debian-based (may lag behind the latest GitHub release)
sudo apt install amass
# macOS
brew install amass
Command Syntax and Complete Examples
Passive enumeration (default, safest, no packets to the target)
amass enum -passive -d example.com
Since -passive behavior is actually the default in v4 (I confirmed this directly in the help output, where -passive is even marked “Deprecated since passive is the default setting”), the equivalent minimal command is simply:
amass enum -d example.com
I ran this exact command against a real domain in my test environment:
timeout 30 ./amass enum -passive -d owasp.org -timeout 1
Actual output:
No assets were discovered
The enumeration has finished
I’m showing you this honestly rather than fabricating a subdomain list: this specific result came back empty because the sandbox this was executed in has network egress restricted to a small allowlist of package registries — DNS resolution and outbound HTTPS to Amass’s actual data sources (crt.sh, VirusTotal, etc.) were blocked at the network layer. On a normal machine with unrestricted outbound internet access, this exact command against a real domain returns dozens to hundreds of real subdomains within seconds to a few minutes. I want you to know exactly what “no assets discovered” means diagnostically, because you may see this same message in a corporate environment with an aggressive egress firewall, and the fix is the same: ensure outbound DNS and HTTPS are permitted.
Active enumeration with brute-forcing
amass enum -active -brute -d example.com -o results.txt
This adds DNS brute-forcing on top of passive collection, and attempts certificate grabbing directly against live hosts — meaning this mode does send traffic to the target’s infrastructure. Only run this against domains you’re explicitly authorized to test.
Multiple domains at once
amass enum -d example.com,example.net,example.org
Restricting or expanding data sources
List every available data source (this is a fully local, offline operation — no network required, which is why I could run and verify it directly):
amass enum -list
Actual verified output (partial, showing real entries):
Data Source | Type | Available
--------------------------------------------------------------------------------
360PassiveDNS api
ASNLookup api
AbuseIPDB scrape *
Active Crawl crawl *
Active DNS dns *
Ahrefs api
AlienVault api *
Alterations alt *
AnubisDB api *
Arquivo archive *
...
DNSDumpster scrape *
DNSHistory scrape *
...
Shodan api
VirusTotal api
Wayback archive
Yandex api
ZoomEye api
I counted the real total: 97 data sources in v4.2.0. The asterisk (*) column indicates sources that are usable without an API key; sources without an asterisk require you to configure API credentials in the config file to activate them. Notice that DNSDumpster itself appears as a native Amass data source — meaning running Amass in passive mode already indirectly queries DNSDumpster’s dataset for you.
To include or exclude specific sources:
amass enum -d example.com -include crtsh,VirusTotal,Shodan
amass enum -d example.com -exclude Bing,Yahoo
IP and ASN targeting
amass enum -d example.com -addr 192.0.2.1-192.0.2.254
amass enum -d example.com -asn 15169
amass enum -d example.com -cidr 192.0.2.0/24
Custom wordlist brute-forcing
amass enum -active -brute -d example.com -w /usr/share/wordlists/subdomains-top1mil-5000.txt
Output control
# Write plain text output to a file
amass enum -d example.com -o subdomains.txt
# Write all output formats with a common prefix
amass enum -d example.com -oA example_scan
# Silent mode - no terminal output, useful for cron jobs / pipelines
amass enum -d example.com -silent -o results.txt
amass intel — target discovery before you have full scope
Find domains and netblocks associated with an organization name:
amass intel -org "Example Corp"
Show IP addresses alongside discovered names:
amass intel -d example.com -ip
Reverse WHOIS to find other domains registered under the same details:
amass intel -d example.com -whois
I verified the real intel flag set directly from the binary’s help output:
-ip Show the IP addresses for discovered names
-ipv4
Show the IPv4 addresses for discovered names
-ipv6
Show the IPv6 addresses for discovered names
-org string
Search string provided against AS description information
-whois
All provided domains are run through reverse whois
Configuration File
Amass supports a YAML configuration file for anything beyond ad-hoc single-domain scans — scope definitions, resolver lists, database connections, and wordlist paths. Here is the real example configuration file shipped inside the official v4.2.0 release archive, which I extracted and read directly:
scope:
domains: # domain names to be in scope
- example.com
ips: # IP addresses to be in scope, multiple methods of inserting ip addresses can be used
- 192.0.2.1
- 192.0.2.2
- 192.168.0.3-8
- 192.168.0.10-192.168.0.20
asns: # ASNs that are to be in scope
- 1234
- 5678
cidrs: # CIDR ranges that are to be in scope
- 192.0.2.0/24
- 192.0.2.128/25
ports: # ports to be used when actively reaching a service
- 80
- 443
blacklist: # subdomains to be blacklisted
- example.example1.com
options:
resolvers:
- "../examples/resolvers.txt" # array of 1 path or multiple IPs to use as a resolver
- 76.76.19.19
datasources: "./datasources.yaml" # the file path that will point to the data source configuration
wordlist: # global wordlist(s) to uses
- "./wordlists/deepmagic.com_top50kprefixes.txt"
- "./wordlists/deepmagic.com_top500prefixes.txt"
database: "postgres://username:password@localhost:5432/database?testing=works"
bruteforce:
enabled: true
wordlists:
- "./wordlists/subdomains-top1mil-5000.txt"
alterations:
enabled: true
wordlists:
- "./wordlists/subdomains-top1mil-110000.txt"
Use it with:
amass enum -config config.yaml
This config-driven approach is what you want for repeated, scheduled, or multi-target scans, since it captures scope, resolver preferences, and database persistence in one version-controllable file rather than a long, error-prone command line.
Real-World Penetration Testing Workflow
Here’s how Amass fits into an actual external assessment, based on the phases I follow:
Phase 1 — Passive-only baseline. Start with an unauthenticated, zero-impact scan to build the initial picture without touching the target’s infrastructure:
amass enum -d target.com -o phase1_passive.txt
Phase 2 — Organizational pivoting with intel. Use the organization name to find sibling domains and netblocks that might not obviously belong to the same root domain:
amass intel -org "Target Corporation" -whois
Phase 3 — Authorized active enumeration. Once scope is confirmed and active testing is authorized, layer on brute-forcing and certificate grabbing:
amass enum -active -brute -d target.com -w custom_wordlist.txt -oA phase3_active
Phase 4 — Liveness validation. Amass tells you what exists in DNS; it doesn’t tell you what’s actually serving content. Pipe the results into httpx:
cat phase3_active.txt | httpx -silent -status-code -title -tech-detect
Phase 5 — Vulnerability scanning on confirmed live hosts. Feed only the confirmed-live hosts into Nuclei to avoid wasting scan time on dead subdomains:
httpx -silent -l phase3_active.txt | nuclei -t cves/ -o nuclei_results.txt
Automation and Integration with Other Tools
Amass + Subfinder deduplication:
amass enum -d target.com -o amass_out.txt
subfinder -d target.com -o subfinder_out.txt
cat amass_out.txt subfinder_out.txt | sort -u > all_subdomains.txt
Amass + Nmap for port discovery on discovered hosts:
amass enum -d target.com -ip -o hosts_with_ips.txt
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' hosts_with_ips.txt | sort -u > ips.txt
nmap -iL ips.txt -T4 -oA nmap_scan
Scheduled diffing for continuous asset monitoring:
#!/bin/bash
# daily_amass_diff.sh
DATE=$(date +%F)
amass enum -d target.com -o "scan_${DATE}.txt"
if [ -f "scan_$(date -d yesterday +%F).txt" ]; then
diff "scan_$(date -d yesterday +%F).txt" "scan_${DATE}.txt" > "diff_${DATE}.txt"
if [ -s "diff_${DATE}.txt" ]; then
echo "New assets detected on target.com:" | mail -s "Amass Diff Alert" you@example.com
fi
fi
Run this daily via cron for lightweight continuous attack surface monitoring — a real pattern used in bug bounty automation setups where catching a newly deployed subdomain early can mean being first to find a vulnerability in freshly deployed code.
Performance Optimization
- Rate-limit DNS queries deliberately. Aggressive querying can trigger rate limiting from resolvers or trip a target’s WAF/IDS. Use
-dns-qpsto cap total queries per second:amass enum -d target.com -dns-qps 20 - Use trusted resolvers for reliability, untrusted resolvers for scale. Amass distinguishes between the two (
-tr/-trffor trusted,-r/-rffor untrusted) — trusted resolvers get priority for verification, untrusted ones absorb bulk query volume. - Limit brute-force depth.
-max-depthcontrols how many subdomain labels deep brute-forcing goes; deep recursive brute-forcing on every discovered name multiplies query volume fast. Use-norecursiveif you just need a first-pass list quickly. - Set a hard timeout for CI pipelines.
-timeout <minutes>prevents a scan from running indefinitely in an automated pipeline:amass enum -d target.com -timeout 30.
Troubleshooting
“No assets were discovered” — As I demonstrated directly above, this happens when outbound network access to Amass’s data sources is blocked (corporate firewall, sandboxed CI runner, VPN restrictions) or when the target genuinely has no discoverable passive DNS footprint. Test basic connectivity first: curl -s https://crt.sh/?q=example.com should return data if your network path is fine.
Build failures with go install — As shown earlier, building from source requires reaching several non-GitHub Go module proxy hosts. Use the precompiled release binary if your environment has restricted egress.
Slow scans — Usually caused by either too many concurrent DNS queries overwhelming your local resolver, or brute-forcing with an oversized wordlist against a large -max-depth. Scale back both.
Missing API-gated sources — Sources without the * marker in amass enum -list need API keys configured in your datasources YAML file to activate; without keys they’re silently skipped, and you won’t get an error, just a smaller data pool.
Best Practices
- Always start passive-only; only move to
-active/-bruteonce you have explicit written authorization for active testing. - Version-pin your Amass releases in team environments — the jump from v4 to v5 changes the module path and may change flags, so unpinned automation can silently break after an update.
- Keep your config YAML in version control alongside scope documentation, so every scan is reproducible and auditable.
- Validate liveness post-enumeration; DNS existence and an actively served, in-scope application are not the same thing.
- Respect the target’s rate limits — this is both an ethical and a practical consideration, since aggressive brute-forcing can look identical to a DNS-based DDoS from the target’s perspective.
Common Mistakes
- Assuming Amass “hacks” anything — it maps, it doesn’t exploit. Pair it with actual scanning/exploitation tools downstream.
- Running
-active -bruteagainst out-of-scope infrastructure by mistake because a wildcard domain pulled in unrelated subdomains — always sanity-check your scope file. - Forgetting that
-passiveis deprecated/default and manually disabling data sources that would have been genuinely useful. - Treating an empty result as “the target has no attack surface” without first verifying your own network path can actually reach Amass’s data sources.
Practical Lab Exercise
- Install the v4.2.0 release binary using the curl/unzip method shown above and confirm with
./amass -version. - Run
./amass enum -listand count how many sources are usable without an API key (look for the*marker) versus how many require one. - Pick a domain you’re authorized to test (or a public bug bounty program’s in-scope domain) and run:
./amass enum -d yourdomain.com -o lab_results.txt - Feed the results into
httpxto check which discovered subdomains are actually live. - Re-run the scan a week later and diff the two output files to see what changed.
FAQ
Is Amass legal to use? Yes, on any domain you own or are explicitly authorized to test (including in-scope bug bounty targets). Active modes send real traffic to target infrastructure, so authorization matters.
Why does amass db or amass viz not work for me? Those subcommands belonged to older major versions. I confirmed directly that v4.2.0 only exposes intel and enum as real subcommands.
Does Amass need API keys to be useful? No — dozens of sources work with zero configuration (marked with * in amass enum -list), though adding API keys for sources like Shodan, VirusTotal, or SecurityTrails meaningfully increases coverage.
What’s the difference between amass intel and amass enum? intel discovers targets (domains, ASNs, netblocks) when your scope isn’t fully known yet; enum performs the actual deep subdomain enumeration once you have defined root domains.
Can Amass replace Nmap? No — Amass maps DNS/asset relationships, not open ports and services. They’re complementary; a common workflow is Amass for host discovery, then Nmap for port/service enumeration on the results.
Summary
OWASP Amass is the closest thing to an industry-standard tool for attack surface mapping — nearly a hundred integrated data sources, a graph-based correlation model, and both passive and active enumeration modes in one binary. I verified its real command structure, its actual data source count (97 in v4.2.0), its genuine configuration file format, and even reproduced real failure modes (network-restricted “no assets discovered” results, and source-build failures from blocked Go module hosts) so you know exactly what to expect and how to troubleshoot it in constrained environments. Used correctly — passive-first, active only with authorization, and fed into downstream tools like httpx and Nuclei — it forms the backbone of a serious external reconnaissance workflow.
References
- Official GitHub repository: https://github.com/owasp-amass/amass
- User’s guide: https://github.com/owasp-amass/amass/blob/master/doc/user_guide.md
- Tutorial: https://github.com/owasp-amass/amass/blob/master/doc/tutorial.md
- OWASP project page: https://owasp.org/www-project-amass/
- Release binaries: https://github.com/owasp-amass/amass/releases
