89036 bc high unbounded unauthenticated pq handshakes permanently terminate node p2p networking and can halt network wide transaction confirmation
Submitted on Aug 16th 2026 at 17:20:14 UTC by @Pig46940 for Audit Comp | Quantus
Report ID: #89036
Report Type: Blockchain/DLT
Report severity: High
Target: https://github.com/immunefi-team/audit-comp-quantus-chain
Impacts:
Network not being able to confirm new transactions (total network shutdown)
Description
Brief/Intro
Quantus applies its incoming-connection limit only after a P2P connection has completed the full handshake. An unauthenticated client can therefore create an arbitrary number of pending connections on the publicly exposed P2P port. After negotiating /noise, each connection also makes the node generate a fresh ML-KEM-768 keypair and sign it with ML-DSA before the client sends its first PQ handshake message. Pending connections consume CPU and one file descriptor each. When the finite descriptor limit is reached, a single EMFILE error is treated as permanent transport termination: the complete litep2p backend, including block announcements, synchronization, transaction propagation, discovery, and all existing peer connections, shuts down and does not restart. The attack is independently repeatable against publicly reachable block-producing nodes; simultaneous targeting can leave the network without a connected block producer capable of establishing transaction confirmations.
Vulnerability Details
1. Pending connections are not included in connection accounting
ConnectionLimits stores only established incoming and outgoing connection IDs:
client/litep2p/src/transport/manager/limits.rs
The admission check compares the configured maximum exclusively with incoming_connections.len():
client/litep2p/src/transport/manager/limits.rs
The transport manager calls this non-reserving check when it receives a pending connection:
client/litep2p/src/transport/manager/mod.rs
The connection ID is inserted into the accounting set only after the handshake succeeds and the connection is accepted as established:
client/litep2p/src/transport/manager/mod.rs
Consequently, if a node has 20 established connections, 1, 1,000, or 100,000 simultaneous pending handshakes all observe the same established count of 20 and pass the admission check. The configured MAX_CONNECTIONS_ESTABLISHED_INCOMING value of 10,000 does not bound pending connections.
Each accepted socket is placed into the pending connection future set and remains live until negotiation succeeds, fails, or times out:
client/litep2p/src/transport/manager/mod.rs
There is no global pending-handshake limit, per-IP limit, or reservation counter.
2. Quantus performs ML-KEM and ML-DSA work before authenticating the client
After the unauthenticated client completes the small multistream negotiation for /noise, the node enters the Quantus pqXX handshake:
client/litep2p/src/transport/tcp/connection.rs
At the beginning of noise::handshake, the listener constructs a new NoiseContext:
client/litep2p/src/crypto/noise/mod.rs
NoiseContext::new generates a fresh ML-KEM-768 keypair and then signs its public key using the node's ML-DSA identity:
client/litep2p/src/crypto/noise/mod.rs
client/litep2p/src/crypto/noise/mod.rs
Only after generating and signing this material does the listener wait for the unauthenticated client's first pqXX message:
client/litep2p/src/crypto/noise/mod.rs
The attacker therefore needs to send only two small multistream frames and does not need a valid PeerId, ML-DSA key, or completed Noise handshake to trigger the node's PQ work. This creates a computational amplification path in addition to linear socket/file-descriptor consumption.
3. One resource-exhaustion error permanently terminates all P2P transports
When the process reaches its file-descriptor limit, accept() returns EMFILE (Too many open files). TcpTransport::poll_next treats any listener error as permanent stream termination:
client/litep2p/src/transport/tcp/mod.rs
The transport manager interprets that None as termination of the installed transports and exits:
client/litep2p/src/transport/manager/mod.rs
There is no retry, listener recreation, backoff, or recovery after descriptors become available again. The process can remain alive while its P2P port is closed and every networking protocol has terminated.
4. The vulnerable listener is intentionally public
This attack does not use the trusted miner-to-node channel. Quantus documentation explicitly states that P2P port 30333 is intended to be publicly reachable, while the miner and RPC ports should remain private. No privileged access, leaked credential, miner authentication, local-network position, or valid P2P identity is required.
The issue is also specific to Quantus's integration: Quantus uses its modified post-quantum litep2p implementation as the network backend and performs ML-KEM-768 generation plus ML-DSA signing in the unbounded pre-authentication path.
Impact Details
Directly reproduced impact
The following effects were reproduced against a local Quantus node:
Hundreds of unauthenticated
/noisehandshakes were admitted concurrently without reaching the configured established-connection ceiling.When the finite descriptor limit was reached in a safely constrained local run, the node logged
Too many open filesand terminated the entire litep2p backend.Synchronization, state requests, transaction propagation, block announcements, Kademlia, identify, ping, and light-client protocols all closed.
After the attacking sockets were released, new connections to the P2P port received
ConnectionRefusedError; the listener did not recover.The node process remained alive. With
--force-authoring, it continued mining an isolated chain that could not be propagated to peers.
Critical network-wide consequence
The exploit is stateless and independently repeatable against every reachable Quantus P2P listener. Node addresses are distributed through ordinary P2P discovery and connection establishment. An attacker can maintain the same pending-handshake load against the reachable block-producing nodes in parallel.
Once the targeted backends terminate:
If block producers follow the normal offline behavior, disconnected miners cannot collectively extend and propagate a canonical chain. If operators enable force authoring, isolated miners can produce local competing branches, but no branch can obtain network-wide confirmation while the partition is maintained. In either case, simultaneously targeting the effective block-producing set leaves the network unable to confirm new transactions.
This maps to the listed Critical impact:
Network not being able to confirm new transactions (total network shutdown)
The report does not claim an unintended permanent chain split requiring a hard fork. Restarting protected nodes and applying normal fork choice should recover from isolated branches; the directly demonstrated persistence is termination of the P2P service until process restart.
Recommended Remediation
References
Proof of Concept
Reproduce descriptor exhaustion and permanent P2P termination
To avoid exhausting the workstation's normal descriptor table, run a separate local node with a deliberately constrained process limit. This does not create the vulnerability; it safely reaches the same finite-resource boundary with fewer local sockets and reproduces the complete failure path.
Run:
Observed client result:
At the same time, the node logged:
This is not merely rejection of excess clients: all installed networking transports and consensus-related protocol services terminate.
Confirm that releasing resources does not restore P2P service
After the first script has closed its sockets, run a single connection attempt:
Observed result:
The process was still running and its RPC server remained available, but its public P2P listener never recovered. A process restart was required. Because the request contains no node-specific state, credential, or fixed PeerId, the same PoC can be independently directed at each reachable block producer. Each affected node closes synchronization, transaction propagation, and block-announcement protocols, producing the network-wide consequence described in the Impact section.
Was this helpful?