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.
Why this matters
The inbound peer-protocol listener (mainnet port 3888) is reachable by default on qtumd nodes. Because the asymmetric-crypto handshake occurs before any authentication or rate-limiting, a single source IP can pin one CPU core, and a small distributed fleet can measurably degrade or deny honest peers' ability to connect. This results in an availability DoS characterised by fullnode peering degradation, though it does not halt consensus (PoS block production is a separate path).
How the attack works
The attack targets the BIP-324 v2 transport responder, which is enabled by default (DEFAULT_V2_TRANSPORT{true} at src/net.h:95). When an inbound connection sends its 64-byte ellswift key, the node immediately executes Initialize (src/net.cpp:1140-1147), triggering a secp256k1 ellswift ECDH plus HKDF-SHA256 key derivation (src/bip324.cpp:41-64). This expensive cryptographic operation runs BEFORE any authentication, allowlist, per-IP rate limit, or proof-of-work gate. The only inbound cap (maxconnections=125) is soft: when full, the node calls AttemptToEvictConnection (src/net.cpp:1815) to evict an existing peer and admit the new socket. As the v2 accept path is single-threaded, a churn of fresh inbound sockets from even one source IP can saturate the path and consume CPU resources.
Affected systems
This behaviour was verified on qtumd v29.1.0 (commit 7641affd / tag v29.1, SHA256 c04e3f49…). The affected component is the P2P inbound transport (BIP-324 v2 responder). The default mainnet P2P port is 3888 (testnet 13888, regtest 23888).
Evidence
Publicly documented No CVE is assigned for this finding.
Independently reproduced by NullRabbit
The reproducer probe_qtum_bip324.py simulates the attack by generating a churn of inbound TCP connections. In "churn" mode, it opens a TCP connection, sends a 64-byte ellswift key to trigger the ECDH, and immediately closes the connection. In "hold" mode, it sends the key and holds the connection to occupy an inbound slot.
Measured under controlled conditions Loopback (isolated regtest, qtumd v29.1.0): The attack held 96.3% average / 100% median of one core over a 60-second run, achieving 14,275 handshakes/s (vs 0.3% baseline). It demonstrated approximately 39.6× egress amplification and filled all 114 inbound slots via eviction-on-full behaviour.
DigitalOcean (production-shape, 4-IP distributed attack): Honest-peer v2 handshake latency increased from a p50 baseline of 32 ms to 3371 ms (107×) under a 4-IP attack, with p99 latency reaching 9656 ms (182×). The test observed 11% outright connection denial at the 4-IP shape. A single attacker IP caused a 65× increase in p50 latency.
Inferred but not reproduced None.
Operator actions
NullRabbit recommends the following mitigations:
- Cost-gate the pre-auth crypto: Apply a per-source-IP budget, cheap proof-of-work, or connection-rate cap BEFORE the ellswift-ECDH runs to prevent unauthenticated peers from forcing unbounded asymmetric-crypto work.
- Make the v2 accept path concurrent: Modify the implementation so a single IP cannot serialise or saturate the accept path.
- Harden the inbound cap: Reserve slots for established or outbound-verified peers so that fresh-socket churn cannot evict honest peers (modifying
AttemptToEvictConnection).
Canonical references
- NRDAX Technique: NRDAX-T0205 (https://nrdax.com/techniques/NRDAX-T0205)
- Advisory: NR-2026-037
- HF Dataset:
nr-bundles-public(primitiveqtum_bip324_prehandshake_ecdh_cpu, 4 bundles) - Reproducer:
probe_qtum_bip324.py - Vendor Source: https://github.com/qtumproject/qtum
Related attacks
This finding is the same class and magnitude as the measured Bitcoin Core BIP-324 finding (BTC_V0_BIP324_PREHANDSHAKE_CPU).
Subscription call to action
Track attacks affecting qtum.
Analysis plus reproducer. No weaponised proof-of-concept code.
Related Posts
What syscall-layer tooling cannot see in P2P infrastructure: a technique-by-technique analysis
If you run Falco, an EDR, or a generic host agent on a validator and a network-DoS attack against the node went undetected, this explains why. A technique-by-technique analysis of five reproduced P2P-infrastructure attacks against the syscall detection surface. Zero are detectable as attributable rules, and the reasons are structural, not a matter of tuning.
How an exposed debug_traceCall lets one unauthenticated request burn seconds of go-ethereum CPU
debug_traceCall re-runs an eth_call-style message opcode-by-opcode with a full EVM tracer attached, so a single small request converts into up to seconds of single-goroutine CPU on the target — and with no per-source rate or concurrency cap, an unauthenticated caller drives many such traces in parallel against an exposed go-ethereum node.
How a zero-value GetBlockHeaders request underflows to serve go-ethereum's entire header chain (CVE-2024-32972)
A go-ethereum eth-protocol peer that sends a GetBlockHeaders request with amount (count) set to 0 triggers an integer underflow — count-1 wraps to UINT64_MAX — bypassing the maxHeadersServe cap and forcing the node to serve headers back to genesis, a publicly disclosed memory-exhaustion DoS (CVE-2024-32972, fixed in geth 1.13.15) that NullRabbit reproduced as a lab detection bundle.
