Skip to content

IntegSec/PortSnatcher

Repository files navigation

PortSnatcher

Catch ephemeral ports the moment they open — fingerprint, hold open, and hand off to the pentester before the window closes.

License: Apache 2.0 Status CI Platform Made by IntegSec

Current status — April 2026 / v1.2.0: the SYN-race capability is real on Linux. RawEngine runs a pnet AF_PACKET SYN spray + pcap SYN-ACK receive + kernel-connect handoff at up to 10,000 pps/target (requires CAP_NET_RAW). macOS and Windows still run the connect-engine with a "raw" label while the BPF / WinDivert ports are written — flagged honestly in BACKEND_STATUS and the CHANGELOG, not hidden. portsnatcher/v1 event schema frozen; scope-file format cross-compatible with IntegSec/agentic-pentest-proxy. Spec · Operator guide · CHANGELOG.


Why PortSnatcher exists

Modern pentest targets expose services on ephemeral, short-lived TCP ports — cloud metadata helpers, auto-scaling admin interfaces, flapping internal services, reconnection-window sockets, debug listeners that open for milliseconds during a deploy. Classic scanners are the wrong shape for this:

  • nmap, masscan, zmap, unicornscan — one-shot. A port that's open for 200ms during your scan is either in the results or isn't; by the time you see it, it's gone.
  • knockd / port-knocking tools — detect, don't exploit.
  • Burp Collaborator / Interactsh — listen on your infrastructure, don't hunt on theirs.
  • pwncat, responder — post-connection, don't race.

PortSnatcher is the first tool purpose-built for the race. It watches a scoped set of targets continuously, wins the race to SYN/ACK the moment a port opens, fingerprints the service on fresh connections (one probe per connection, nothing destructive unless authorized), stands up a local hold-open tunnel so you can attack through Burp or any TCP-speaking tool before the port closes, and pokes you via desktop toast / webhook the moment it catches.

It's the tool you reach for when nmap gave you nothing but you know services are flashing open on that host.

The 30-second demo (target UX)

# On the pentester's laptop — targeting a scope-file-defined engagement
$ portsnatcher --config engagement.toml
[09:14:02] PortSnatcher v1.2.0 — engagement ENG-2026-0142
[09:14:02] Profile: internal  |  Engine: raw  |  Targets: 10.20.0.0/16 (256 hosts)
[09:14:02] Ports: ephemeral-iana (49152-65535)  |  Rate cap: 50000 pps
[09:14:02] Event bus: http://127.0.0.1:7177/events  (token in ~/.config/portsnatcher/bus-token)
[09:14:02] Scope file: ./scope.json (compatible with agentic-pentest-proxy)
[09:14:02] Watching...

[09:14:47] CATCH  10.20.5.17:54283  (window: 180ms)  engine=raw  syn_rtt=2ms
           └─ fingerprint: HTTP/1.1, Server: nginx/1.25.3, TLS: no
           └─ hold-open:   localhost:7101  (dumb_tunnel, keepalive engaged)
           └─ desktop toast sent, webhook POST ok
           → attach Burp to 127.0.0.1:7101 to work the service

From port opening to live Burp-ready tunnel: typically ~2 seconds. The toast fires while the fingerprinter is still running — the tunnel is up before the banner shows up on your screen.

How it works

 targets.json  ────┐
                    │
 scope.json   ────┐ │      ┌─────────────────────────────────────────┐
                  │ │      │           PortSnatcher                  │
 config.toml  ────┼─┴──────┤                                         │
                  │        │  ┌──────────┐   ┌──────────────┐        │
                  └────────┼──▶ Engine   │──▶│ Fingerprinter│        │
                           │  │(raw|conn)│   │ (probe ladder│        │
                           │  └──────────┘   └──────────────┘        │
                           │        │              │                 │
                           │        ▼              ▼                 │
                           │   ┌─────────────────────────┐           │
                           │   │    Event Bus (v1)       │           │
                           │   │  tokio::broadcast +     │           │
                           │   │  SSE/WebSocket server   │           │
                           │   └──────┬──────────────────┘           │
                           │          │                              │
                           │  ┌───────┼────────┬───────────┐         │
                           │  ▼       ▼        ▼           ▼         │
                           │ TUI   JSONL  Toast/Webhook  Hold-Open   │
                           │                              Proxy      │
                           │                                │        │
                           └────────────────────────────────┼────────┘
                                                            ▼
                                              127.0.0.1:71xx (attach here)

Everything communicates through a versioned JSON event bus. The same stream you see in the TUI is what a webhook receives, what events.jsonl records, and what the future Burp extension will subscribe to over HTTP.

Key features

RawEngine (Linux, real SYN race since v1.2) pnet AF_PACKET SYN spray + pcap SYN-ACK receive + kernel-connect handoff. Sub-100ms detection of ephemeral ports at up to ~10,000 pps/target. Requires CAP_NET_RAW. On macOS/Windows the same engine falls back to a connect-labelled scheduler pending the BPF / WinDivert ports (v1.3).
ConnectEngine Unprivileged async connect() with FuturesUnordered concurrency. Works in containers, locked-down jumpboxes, CI. Same events, same pipeline. Use when you don't have CAP_NET_RAW.
Probe ladder, not probe list Passive banner read first (zero bytes sent), then TLS ClientHello, then port-aware protocol probes — one per fresh connection. Never chains a destructive probe after a soft one. Nine probes in v1 (HTTP, TLS, SSH, Redis, Mongo, Postgres, SMB, banner).
Hold-open tunnel The moment a port is caught, we stand up localhost:71xx piping bytes to the target. You point Burp / ncat / your custom exploit at it before the port closes. Optional TLS MITM for HTTPS catches, using a reusable on-disk CA.
Scope-aware probes Probes are tagged with technique categoriesrecon, web_app, api_testing, ssl_tls, destructive. Probes without authorized coverage are skipped and audit-logged.
Stable event schema portsnatcher/v1 events are a frozen JSON contract. Additive-only forever. Your Burp extension, SOC pipeline, or custom tooling can depend on it.
Real-time handoff Desktop toasts, Slack/Discord/ntfy/PagerDuty webhooks, terminal TUI (--tui), SSE/WebSocket bus — all fed from the same stream.
Safety first Scope file required; hard-deny on loopback/link-local; global + per-target pps caps; explicit-target rule; --i-know-what-im-doing flag for overrides (and yes, it's literally named that). Every decision logged to audit.log.

Cross-tool scope compatibility

PortSnatcher consumes the same JSON scope-file format as IntegSec's agentic-pentest-proxy. One file describes your engagement; both tools honor it. PortSnatcher adds a namespaced portsnatcher section for port-specific policy that the proxy ignores cleanly.

{
  "engagement_id": "ENG-2026-0142",
  "client": "Acme Corp",
  "operator": "you@yourshop.com",
  "authorized_targets": {
    "ip_ranges": ["10.10.10.0/24"],
    "domains": ["*.acme.com"]
  },
  "excluded_targets": ["10.10.10.99"],
  "authorized_techniques": ["recon", "web_app", "api_testing", "ssl_tls"],
  "excluded_techniques": ["dos", "destructive"],
  "engagement_window": {
    "start": "2026-04-22T08:00:00Z",
    "end":   "2026-05-06T17:00:00Z"
  },
  "portsnatcher": {
    "port_policy": {
      "include": ["ephemeral-iana", "22", "80", "443"]
    }
  }
}

Profiles — three postures, one codebase

internal external ctf
Default engine raw raw raw
Global pps cap 50,000 1,000 100,000
Per-target cap 2,000 200 10,000
Probe ladder full minimal full + aggressive
Toast on catch yes no yes
Scope file required yes yes (strict) no (lab)

--profile is a one-flag way to pick a coherent default posture. Override any individual setting when you need to.

Roadmap

Shipped (v1.0 → v1.2)

  • Design + five phase plans (spec)
  • ps-core (scope, config, frozen portsnatcher/v1 event schema, insta snapshots)
  • ps-engineConnectEngine + RawEngine
  • ps-fingerprint — nine-probe ladder with fresh-connection discipline
  • ps-proxy — dumb tunnel + TLS MITM with rcgen-signed CA
  • ps-bus — SSE + WebSocket + bearer-token auth
  • ps-notify — terminal / JSONL / webhook / desktop sinks
  • ratatui TUI via --tui
  • Linux / macOS / Windows CI matrix + fmt + clippy gate
  • Fuzz targets + cargo-deny license policy
  • Signed cargo-dist-style prebuilts via GitHub Releases
  • Live-engagement E2E smoke test
  • Real Linux SYN race (v1.2): pnet AF_PACKET + pcap + kernel-connect handoff

v1.3 — next

  • macOS BPF port of SynRace
  • Windows WinDivert port of SynRace
  • IPv6 support in target plan, scope guard, and SynRace
  • Multi-NIC per-target source-IP routing

v1.4+

  • First-party Burp Suite extension (Montoya API) subscribing to the event bus
  • Caido / ZAP plugins following the same pattern
  • Web dashboard for the event bus
  • crates.io publish (cargo install integsec-portsnatcher) once the registry token is configured

v2+

  • UDP support
  • Deeper protocol-specific probe modules (LDAP, Kerberos, proprietary binary protocols)
  • Distributed mode (multiple PortSnatcher probes feeding one bus)

Safety and ethics

PortSnatcher is a professional pentest tool, not a script-kiddie toy. It refuses to run without a scope file. It refuses internet-wide scanning. It logs every decision — including every packet it chose not to send — to an audit trail that belongs in your client report. The defaults are conservative; the bypass flag is intentionally awkward to type.

Use it on engagements you're authorized to perform. If your engagement letter doesn't cover PortSnatcher's behavior, don't run it. The Apache-2.0 license gives you rights to the code; it does not give you rights to the networks you point it at.

Supported platforms

Linux macOS Windows
ConnectEngine yes yes yes
RawEngine — real SYN race (v1.2+) yes (pnet AF_PACKET + pcap) not yet (v1.3: BPF) not yet (v1.3: WinDivert)
RawEngine — connect-labelled fallback only if SynRace fails to init yes (current) yes (current)
Optional nftables RST-drop kassist installed when nft available n/a n/a
Requires elevation for RawEngine CAP_NET_RAW (setcap cap_net_raw,cap_net_admin=eip) root admin

Linux is where the headline capability actually lives today. macOS and Windows are solid for the ConnectEngine, the hold-open proxy, TLS MITM, the TUI, and the full event-bus stack — they just don't do the real sub-100ms SYN race yet. That's v1.3.

Honest check: the real race only improves catch-rate for ports that open for milliseconds-to-seconds. If your target is serving a long-lived HTTPS endpoint on 443, the ConnectEngine on any OS is already fine.

Install

Prebuilt binary (recommended). Grab the archive for your OS+arch from the latest GitHub Release and unpack it somewhere on your $PATH. Per release, we ship:

  • portsnatcher-v1.2.0-x86_64-unknown-linux-gnu.tar.gz
  • portsnatcher-v1.2.0-x86_64-apple-darwin.tar.gz
  • portsnatcher-v1.2.0-aarch64-apple-darwin.tar.gz
  • portsnatcher-v1.2.0-x86_64-pc-windows-msvc.zip

On Linux, grant the real-SYN-race engine the capabilities it needs (otherwise it falls back to the connect-labelled scheduler):

sudo setcap cap_net_raw,cap_net_admin=eip "$(which portsnatcher)"

Building from source:

git clone https://github.com/IntegSec/PortSnatcher
cd PortSnatcher
cargo build --release --bin portsnatcher
./target/release/portsnatcher --help

cargo install will land once the CARGO_REGISTRY_TOKEN repo secret is configured (v1.4 roadmap):

cargo install integsec-portsnatcher  # NOT YET published to crates.io

Contributing

PortSnatcher is open to community contributions. Two rules are non-negotiable:

  1. The portsnatcher/v1 event schema is frozen. Adding a new optional field is OK. Removing or renaming a field is a v2 bump and requires coordinated consumer updates. The insta snapshots in crates/ps-core/tests/snapshots/ are the enforcement mechanism — a PR that changes them must explain why.
  2. Scope-file compatibility with agentic-pentest-proxy is a stability contract. Anything that changes the top-level fields is a breaking change.

See CONTRIBUTING.md for dev setup, commit conventions, and the PR checklist. For security-sensitive reports, see SECURITY.md.

Prior art and influences

PortSnatcher stands on the shoulders of:

  • nmap, masscan, zmap, unicornscan — port-scanning state of the art
  • pnet — the raw-socket + pcap crate that makes RawEngine tractable on Linux
  • rustls — the TLS implementation used for the MITM proxy
  • ratatui, tokio, axum — Rust's superb async ecosystem
  • rcgen — on-disk CA generation for the TLS MITM
  • Burp Collaborator / Interactsh — prior art for out-of-band handoff patterns
  • IntegSec's agentic-pentest-proxy — sister tool and source of the scope-file format

We are not aware of any prior tool combining continuous SYN-level racing, short-window fingerprinting on fresh connections, and pentester-in-the-loop hold-open handoff in one package. If you know of one, open an issue — we'd love to credit it here.

License

Apache License 2.0 — see LICENSE and NOTICE.


Built by IntegSec — our first open-source Rust tool. Found a bug, want a feature, or know a use case we haven't thought of? Open an issue or start a discussion — we want the pentest community shaping this.

About

Catch ephemeral ports the moment they open — fingerprint, hold open, and hand off to the pentester before the window closes. Apache-2.0.

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages