Skip to content

Repository files navigation

portwave

Fast IPv4/IPv6 port scanner with built-in HTTP(S) enrichment, SSL recon, and nuclei — one binary, no subprocess chain.

License: MIT Rust Platform X / Twitter

                 _
  _ __   ___  _ __| |___      ____ __   _____
 | '_ \ / _ \| '__| __\ \ /\ / / _` |\ / / _ \
 | |_) | (_) | |  | |_ \ V  V / (_| | V /  __/
 | .__/ \___/|_|   \__| \_/\_/ \__,_|\_/ \___|
 |_|     portwave · by assassin_marcos

Takes IPs, CIDRs, ranges, domains, or ASNs — mixed freely. Wildcard-DNS pre-filter, parallel resolution, adaptive Phase-A scan, native HTTP(S) probe, SSL/SAN recon, nuclei. Resume-safe, diff-aware, single static binary.


Install

# Linux / macOS
git clone https://github.com/assassin-marcos/portwave && cd portwave && bash install.sh

# Windows
git clone https://github.com/assassin-marcos/portwave; cd portwave; powershell -ExecutionPolicy Bypass -File .\install.ps1

# Self-manage
portwave -u    # install latest
portwave -c    # check for updates
portwave -X    # uninstall

Quickstart

First positional is a folder name for results (./scans/<folder>/).

portwave scan 1.2.3.4                              # one IP
portwave scan 203.0.113.0/24                       # CIDR
portwave scan -d example.com                       # domain (CDN auto-skip)
portwave scan -d "a.site.com,b.site.com"           # multiple
subfinder -d target.com -silent | portwave bb -i - # subdomains via stdin
portwave scan -a AS13335 --ipv4-only               # full ASN, v4 only
portwave scan 203.0.113.0/24 -p 22,80,443          # custom ports
portwave scan example.com --all-ports               # exhaustive TCP coverage
portwave scan x -i list.txt --top-ports 100        # mixed targets, top-100
portwave big -a AS99999 --max-scan-time 30m --max-pps 200   # rate-limited

Defaults are tuned for fast + accurate. A bare scan uses the curated port list, not all 65535 TCP ports; use --all-ports when exhaustive coverage is required. Small scopes automatically pace simultaneous probes per host so a single target is not hit with the whole list at once.


v0.19.0 missed-open-port accuracy

  • Small-scope pacing. Single-host and small-scope scans cap simultaneous probes per host, preventing burst loss while broad scans retain global concurrency.
  • Safer adaptive start. Phase A starts at 1000 workers and grows toward 3000 when the local network remains healthy.
  • Priority coverage. Application ports 3000, 8181, and 9112 are scanned early, and the bundled list now includes 9112.
  • Exhaustive mode. --all-ports scans the complete TCP range, while normal output states the active port coverage.
  • Port-list freshness. Legacy repository paths no longer silently override the embedded list unless --port-file is explicit.

v0.18.12 SSL-root accuracy

  • Google certificate noise filtering. Country-specific Google roots and Google-owned Android, analytics, commerce, and YouTube aliases are excluded from ssl_root_domains.txt.
  • Target preservation. Filtering remains exact and case-insensitive, so similarly named customer domains and the actual scan target remain visible.
  • Regression coverage. The observed certificate roots and representative lookalike customer domains are covered by automated tests.

v0.18.11 accuracy fixes

  • Evidence-aware reporting. TCP handshakes are separated from protocol-confirmed services; port-number mappings remain service_guess hints.
  • Mass TCP acceptors. Hosts that accept unusually broad port ranges without protocol evidence are flagged, and their sockets are not promoted to confirmed services.
  • Bounded enrichment. Suspected mass acceptors receive focused HTTP validation by default; --probe-all-tcp-accepted restores exhaustive probing when required.
  • Resume safety. Prior results are revalidated instead of being preserved as current open ports without a new probe.
  • Incomplete-scan safety. Probes exhausted by local resource pressure mark the scan incomplete and return exit code 2 instead of silently disappearing.
  • Downstream accuracy. HTTP target and nuclei output defaults to confirmed HTTP responses; --nuclei-all-ports remains the explicit broad-probing override.

Pipeline

0. DNS / wildcard filter — resolve domains in parallel; collapse wildcard zones
1. Phase A   — adaptive TCP connect; records TCP acceptance, not a guessed service
2. Phase B   — banner grab + TLS sniff; confirms protocols from response evidence
3. Pass-C    — native HTTP(S) probe with mass-acceptor budgeting
4. SSL recon — handshake reuse from Pass-C → SAN + issuer extraction
5. nuclei    — template-driven vulnscan on confirmed HTTP targets (if installed)

HTTP/2 via ALPN, permissive TLS (bundled OpenSSL — accepts self-signed, expired, hostname-mismatched, malformed certs so the scanner sees responses, not handshake errors).

Domains. Resolved in parallel via hickory DNS (15 trusted upstreams). Wildcard zones (*.example.com → 1.2.3.4) are detected by 3 random-label probes per parent suffix and collapsed to one representative — typically skips ~90 % of DNS work on big subdomain lists. Domains landing on a known CDN edge (Cloudflare, Akamai, Fastly, CloudFront, Gcore, Imperva, etc.) are skipped by default. Use --cdn-enrich to keep them: the shared edge IPs are not port-scanned (pure waste), but the hostname is sent straight to HTTP(S) enrichment so you still get status / title / tech / redirect for the app behind the edge. --allow-cdn instead does a full port scan of the edge IPs (rarely useful, and socket-heavy on large lists). Refresh CDN list with portwave --refresh-cdn.


Output (./scans/<folder>/)

File Contents
open_ports.jsonl TCP acceptance plus confirmation evidence — protocol is observed, while service_guess is only a port-number hint
enrichment_results.txt URL [status] [length] [title] for responding HTTP targets only
http_targets.txt Confirmed HTTP URL list fed to nuclei
ssl_findings.txt [ssl-dns-names] lines per unique cert (nuclei-ssl format)
ssl_root_domains.txt Unique eTLD+1 domains aggregated across SAN entries — third-party platform roots (Google/Microsoft/Cloudflare/AWS SSO, Firebase/App Engine/Cloud Run, CDNs, social embeds) are filtered out so only the target's own surface remains
nuclei_results.txt nuclei findings
scan_summary.json Separate TCP-accepted, confirmed-service, incomplete-probe, timing, and classification totals
scan_diff.json New / closed opens vs the last run in this folder
domains.json / origin_domains.txt Domain resolution + CDN tagging (when -d/-i used)
--- OPEN PORTS (8 total across 1 host) ---
  example.com → 203.0.113.5
      :22    [ssh]
      :80    [http]   HTTP/1.1 301 Moved Permanently  · "301 Moved Permanently"
      :443   [https]  HTTP/1.1 200 OK                 · "Acme Dashboard"
─── enrichment 2 target(s) · 0.35s   ✓ 2 responding · 1 2xx · 1 3xx
─── ssl recon 1 unique cert · 0.00s  → ssl_findings.txt

IPv6

A /64 is 2⁶⁴ addresses. Exhaustive scanning is impossible.

  • Any scope > 2²⁰ (≈1 M) hosts is refused with three bypass options.
  • --smart-ipv6 replaces ranges > /108 with ~450 RFC-7707 likely-addresses (hexspeak, low-sequential, SLAAC landmarks). /32 becomes a minute.
  • --allow-huge-scope overrides explicitly.
portwave gcloud 2a00:1450::/32 --smart-ipv6 --top-ports 10

Common flags

Full list via portwave -h. Most-used:

Flag Default Purpose
-d, --domain Comma-separated domains
-i, --input-file Mixed-target file (- for stdin)
-a, --asn ASN list, expanded via RIPE stat
-e, --exclude Ranges to skip
-p, --ports / --top-ports N bundled Custom ports / top-N from bundled list
--all-ports off Scan all 65535 TCP ports; explicit because it increases traffic and duration
-U, --udp off UDP discovery on well-known ports
-t, --threads 1000 Phase-A pool (adaptive grows to 3000 / 1.5×)
--per-host-concurrency 256 Small-scope simultaneous-probe cap per host; 0 disables it
-T, --timeout-ms 800 Phase-A connect timeout
-r, --retries 1 Retries on Phase-A timeouts
--enrich-timeout-ms 1500 Phase-B banner timeout
-C, --probe-concurrency 150 HTTP probe concurrency
--no-follow-redirects follow Disable 3-hop redirect chain
--no-enrich / --no-banner / --no-tls-sniff all on Disable Phase-B/C steps
--no-ssl-scan on Skip SAN/issuer extraction
--no-wildcard-filter on Resolve every input even on wildcard zones
--no-nuclei on Skip nuclei
--nuclei-all-ports off Send every TCP-accepted candidate to nuclei, including unconfirmed ports
--probe-all-tcp-accepted off Disable mass-acceptor HTTP budgeting and probe every TCP-accepted candidate
--ipv4-only / --ipv6-only both Family filter
--smart-ipv6 / --allow-huge-scope off IPv6 scope handling
--cdn-enrich skip CDN domains: enrich hostname on 80/443, no port scan
--allow-cdn skip Full port-scan of CDN edge IPs (socket-heavy)
--max-pps / --max-scan-time Packet-rate / wallclock cap
--dry-run Print scan plan + exit
-n, --no-resume resume Wipe prior artefacts, start fresh
-o, --output-dir ./scans Output root
-w, --webhook --webhook-on-diff-only POST summary on completion
--json-out NDJSON to stdout
-q, --quiet --no-art --no-update-check
-u / -c / -X / --refresh-cdn Self-management

Limitations

  • TCP-connect only (no SYN scan — no raw sockets, no root)
  • No service-version fingerprinting past 9-label protocol classify (chain nmap -sV if needed)
  • No IDS evasion (no decoys, fragments, source spoofing)
  • No ICMP host-discovery pre-flight
  • No passive subdomain enumeration — pair with subfinder -silent | portwave -i -

FAQ

cdn:fastly next to a port? The IP is in a published CDN edge range. The port belongs to the CDN, not the origin. --allow-cdn to scan anyway.

Heavy local_err / adaptive shrinks on macOS? Your shell's FD soft limit is too low (default 256 from launchctl). Run ulimit -n 65535 before portwave, or upgrade — recent versions auto-target the kernel ceiling.


License / Contact

MIT. Developed by @assassin_marcos. Issues + PRs at https://github.com/assassin-marcos/portwave/issues. nuclei (ProjectDiscovery) is resolved at scan time via PATH or $PORTWAVE_NUCLEI_BIN.

Disclaimer: Security-research tool. Only scan systems you own or have written permission to test.

About

Ultra-fast hybrid IPv4/IPv6 port scanner with adaptive concurrency, banner grab, TLS sniff, and built-in httpx + nuclei recon pipeline — written in async Rust.

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages