CometBFT runs the SecretConnection crypto handshake before authenticating peers

simonmorley1 pts0 comments

How CometBFT's SecretConnection handshake spends CPU before authenticating the peer | NullRabbit

← Back to ResearchA CometBFT node runs the full SecretConnection STS handshake — an X25519 Diffie–Hellman exchange (computeDHSecret) and an Ed25519 signature verification (remPubKey.VerifySignature) — for any peer that connects, before the caller checks whether that peer's node-ID is in the node's persistent/allowlist set and disconnects it. An unauthenticated remote source therefore forces the node to spend its asymmetric-crypto budget with no valid signing key required; NullRabbit measured one attacking host driving a single-validator cometbft v0.38.22 node from a 2.6% CPU baseline to 286.3% CPU (roughly three cores).

NRDAX-T0205 ("pre-handshake-crypto-cpu-burn") is a technique NullRabbit tracks across several chains and implementations, each recorded as its own instance. This brief documents only the CometBFT / Cosmos instance measured here — it is not a claim that NullRabbit reproduced, or that the mechanism is identical, on any other listed chain.

Why this matters

SecretConnection is the upstream P2P transport for the entire Cosmos-SDK family — roughly 100 chains build on the same cometbft/cometbft P2P engine. The attack needs no valid node key, no stake, and no prior relationship with the target: any TCP client that can reach the P2P listener (default port 26656) can drive the server through the expensive half of the handshake and then simply disconnect. By default CometBFT applies no per-IP connection or handshake rate-limit before that crypto runs, so a naïve, generic rate limit placed anywhere downstream of the TCP accept doesn't help — the cost is already paid by the time a request-level limiter would see anything to reject. The advisory rates this MEDIUM : it is a single-node CPU/availability issue, not a crash and not a consensus-safety or funds issue, but a validator operator whose node is the one being flooded still has to absorb the load.

How the attack works

Entry point. The CometBFT P2P listener (default tcp://0.0.0.0:26656), specifically the SecretConnection STS handshake performed by MakeSecretConnection in p2p/conn/secret_connection.go.

Preconditions. A reachable P2P listener. The connecting side needs nothing but a freshly generated Ed25519 keypair — no allowlisted node-ID, no stake, no prior handshake.

Processing path. MakeSecretConnection runs, in order: generate an ephemeral X25519 keypair, exchange ephemeral public keys, compute the shared secret via computeDHSecret (secret_connection.go:124), derive the transcript/HKDF and AEAD keys, sign a challenge, and finally verify the remote peer's Ed25519 signature via remPubKey.VerifySignature (secret_connection.go:178). Only after MakeSecretConnection returns does the caller, in p2p/peer.go, compare the now-known remote node-ID against the node's allowlist and disconnect an unwanted peer.

Resource boundary crossed. The node's asymmetric-crypto CPU budget — the X25519 DH and the Ed25519 verify are both paid before any authentication check runs. The package's own docstring (secret_connection.go:58-61) documents that "consumers of the SecretConnection are responsible for authenticating the remote peer's pubkey against known information, like a nodeID. Otherwise they are vulnerable to MITM" — it does not name the second consequence, that the same pre-authentication ordering lets any unauthenticated source spend the node's crypto budget at will.

Resulting behaviour. Repeating the handshake from a worker pool sustains elevated CPU on the target for as long as the flood runs. In NullRabbit's single-validator measurement (cometbft v0.38.22, --proxy_app=kvstore, 32 workers, 30 s), one attacking host drove 1,689,798 probes that reached server-side crypto (56,326 probes/sec), of which 26,999 completed a full two-sided handshake; server CPU rose from a 2.6% baseline to 286.3% (roughly three cores) with RSS rising from 51 MB to 225 MB (+174 MB), and cleanly recovered to 6.4% CPU within 5 seconds of the flood stopping. Block production continued at ~1 block/sec (30 blocks committed) throughout.

Affected systems

Chain: Cosmos — SecretConnection is the shared upstream P2P transport for the Cosmos-SDK family; the advisory characterises it as affecting "every CometBFT-based chain" (~100 chains build on the same engine). Only the reference cometbft binary was independently measured; this is not a per-downstream-chain measurement.

Implementation: CometBFT (cometbft/cometbft).

Component / method: P2P SecretConnection STS handshake, p2p/conn.MakeSecretConnection, default listener port 26656.

Version measured: cometbft v0.38.22, on a self-owned single-validator localnet (--proxy_app=kvstore) and separately on a 4-validator localnet quorum (cometbft testnet --v 4). No per-IP handshake rate-limit is applied before the crypto runs, so the default configuration is affected.

Vendor response: Contact security@cometbft. The vendor scopes node-level...

cometbft node handshake secretconnection crypto runs

Related Articles