rusty-kaspa gRPC pre-auth HTTP/2 stream flood causing inbound-peering exhaustion and eviction
A pre-auth HTTP/2 stream flood on the rusty-kaspa tonic gRPC P2P endpoint exhausts inbound peering capacity by evicting honest peers and denying new admissions.
Why this matters
This attack degrades inbound-peering availability for rusty-kaspa nodes. A single unauthenticated host can evict all honest inbound peers and block new connections using minimal resources, bypassing the intended --maxinpeers limit due to lazy enforcement.
How the attack works
The attack targets the rusty-kaspa tonic gRPC P2P service (/protowire.P2P/MessageStream). The connection_handler.rs:222 NewPeer admission path lacks an admission-time gate: it applies no TLS, IP-allowlist, ban check, per-IP cap, or concurrency layer. The only inbound bound (--maxinpeers) is enforced lazily and randomly by the connection manager (connectionmanager/lib.rs:247) on a ~30-second tick. An attacker opens ~512 pre-auth HTTP/2 streams. This causes two effects: (1) at the 30-second tick, random eviction removes 100% of honest inbound peers; (2) the serial Hub event loop (hub.rs:45) creates a head-of-line stall that denies 100% of honest inbound admission. These 100% figures were measured on a devnet node configured with --maxinpeers=8; behaviour at the production default (128) was not separately measured.
Affected systems
- Chain: Kaspa
- Implementation: rusty-kaspa (commit 3cef6adaf2c79cfa34704070a49fe10d712b5729)
- Components:
p2p: tonic gRPC P2P service (/protowire.P2P/MessageStream),connection_handler.rs: NewPeer admission,connectionmanager: inbound eviction loop - Version: 1.1.0 (measured)
Evidence
- Publicly documented: None (original NullRabbit research, no CVE assigned).
- Independently reproduced by NullRabbit: Yes, via the reproducer
kaspa-h1-harness.py(primitivekaspa_grpc_h2_preauth_stream_flood). - Measured under controlled conditions: Finding KAS-H1, measured 2026-06-02. A single host opening ~512 pre-auth HTTP/2 streams against a devnet node (rusty-kaspa 1.1.0,
--maxinpeers=8) resulted in:- 100% of honest inbound peers evicted at the random 30-second tick (t=14.3 s).
- 100% of honest inbound admission denied via serial-Hub head-of-line stall over an 18 s window.
- Node RSS increase from 47 MB to 172 MB (+125 MB).
- Node CPU approximately 0%.
- Inferred but not reproduced: Not established.
Operator actions
- Add an admission-time gate before
NewPeer(connection_handler.rs:222): enforce per-IP connection and stream caps, consult the ban list, and require TLS or a handshake-proof before admission. - Make
--maxinpeersa real synchronous admission bound at accept time, rather than a lazy random-eviction tick (connectionmanager/lib.rs:247). - Reserve inbound slots for established or outbound-verified peers.
- Parallelize the Hub accept path to prevent head-of-line stalls.
Canonical references
- NRDAX technique: https://nrdax.com/techniques/NRDAX-T0064
- NullRabbit advisory: https://github.com/NullRabbitLabs/nullrabbit-advisories/tree/main/NR-2026-041
- HF evidence bundle: https://huggingface.co/datasets/NullRabbit/nr-bundles-public
Related attacks
- NRDAX-T0064 instances:
cosmos_grpc_stream_flood(Cosmos)ic_xnet_concurrency_cap(Internet Computer)iota_grpc_stream_cap_dos(IOTA)http2_settings_ack_stream_cap_bypass(HTTP/2)
Subscription call to action
Track attacks affecting Kaspa and other decentralised infrastructures by subscribing to NullRabbit briefs.
Analysis plus reproducer. No weaponised proof-of-concept code.
Related Posts
Casper P2P TLS-1.3 P-521 pre-auth ECDSA verify burn
An unauthenticated peer can force a Casper node to run a full P-521 ECDSA signature verification during TLS-1.3 handshake — before any admission gate, allowlist, or per-peer cap — burning crypto-CPU via fresh-cert-per-connection churn.
Walrus storage-node HTTP/2 Rapid Reset: memory exhaustion via u32::MAX default
Walrus storage-node's HTTP/2 server ships with `http2_max_pending_accept_reset_streams = u32::MAX`, disabling the post-CVE-2023-44487 accounting limit. An unauthenticated attacker forces rapid stream resets, accumulating unbounded pending-accept-reset state → memory pin → OOM with no GOAWAY to shed load.
Qtum BIP-324 Pre-Authentication ECDH CPU Exhaustion
Qtum's BIP-324 v2 transport runs a pre-authentication ellswift-ECDH on every inbound connection, allowing a single source IP to saturate a core and deny honest peers.
