For the complete documentation index, see llms.txt. This page is also available as Markdown.

88939 bc medium unauthenticated pq handshakes increase validator cpu consumption by 39 before peer authentication and established connection accounting

Submitted on Aug 15th 2026 at 19:59:19 UTC by @coinsspor for Audit Comp | Quantus

  • Report ID: #88939

  • Report Type: Blockchain/DLT

  • Report severity: Medium

  • Target: https://github.com/immunefi-team/audit-comp-quantus-chain

  • Impacts:

    • Increasing network processing node resource consumption by at least 30% without brute force actions, compared to the preceding 24 hours

Description

Brief/Intro

An unauthenticated remote host can make a Quantus validator perform expensive post-quantum handshake work before peer authentication and before the connection is represented in the established incoming-connection accounting. Sending only the canonical 28-byte multistream negotiation for /multistream/1.0.0 followed by /noise is sufficient to trigger the frozen node's per-connection PQ handshake materialization. Under the default non-reserved P2P policy on the frozen production-profile node, the final ten-pair experiment measured a 39.3084% mean CPU increase, with a Student-t 95% confidence interval of 38.2422%–40.3745%. The conservative lower bound, 38.2422%, remains materially above the selected 30% Medium threshold.

Vulnerability Details

Pre-authentication PQ work

The frozen in-scope chain commit is:

3b243b870a5442a6e5f443056f67bbba87d472f5

The incoming P2P path constructs a new NoiseContext before reading the remote peer's first cryptographic Noise/KEM handshake message.

At construction time, Quantus performs the expensive local PQ preparation used by the handshake, including fresh ML-KEM state and ML-DSA identity/signature materialization.

The attacker-controlled trigger is only the normal multistream protocol selection:

which encodes:

After these 28 unauthenticated bytes are sent, the attacker can stop before providing a valid cryptographic handshake message.

The attacker does not need to generate an ML-KEM key, create an ML-DSA signature, authenticate a peer identity, own a Quantus account, construct a blockchain transaction, or guess/search any credential or secret.

Established-connection accounting does not reserve pending handshakes

Quantus has an incoming-connection limit, but the relevant limiter accounts for entries in the established incoming_connections set.

The effective ordering is:

The expensive PQ work therefore occurs while the remote peer is still unauthenticated and before that peer consumes a slot in the established incoming-connection accounting.

Fresh pending-handshake checks completed 10, 100, and 1,000 simultaneous probes with zero failures. The final impact experiment requires only 400 concurrent sockets.

Normal validator P2P reachability

The issue does not depend on optional public RPC exposure.

The frozen Quantus node's normal P2P configuration exposes TCP P2P listeners for network participation. The final authoritative experiment uses the default non-reserved P2P policy; --reserved-only is not used by the final submission PoC.

Quantus-specific root cause

This report is not asserting a generic TCP or upstream libp2p DoS.

Quantus PR #574 / commit:

d4579e063b281615ca9301b599e9f47620c07b41

vendored litep2p, made it the default networking backend, and moved Quantus PQ handshake materialization into each newly created NoiseContext.

As a supplementary same-machine historical control, the preceding Quantus PQ transport at:

a735879a76d34811c6631b64f529b01b2c195247

was built with:

and tested on the same machine with the identical 28-byte protocol negotiation. It showed a 3.6651% mean CPU increase, with 95% CI [3.4708%, 3.8593%].

Because this historical control is a different commit and was built in release profile, it is not used to establish the selected 30% threshold. It is supporting evidence only, providing evidence that generic TCP/multistream handling alone does not explain the frozen target's approximately 39% increase.

Attacker/victim cost

The final workload opens 4,000 partial handshakes in batches of 400 and sends only:

A separate production-profile cost measurement recorded:

  • attacker CPU: 5.41 CPU-seconds

  • incremental victim CPU above baseline: 17.80 CPU-seconds

  • incremental victim/attacker CPU ratio: 3.29×

The primitive is deterministic protocol-level resource amplification. It does not perform brute-force guessing or search.

Impact Details

Selected in-scope impact:

Increasing network processing node resource consumption by at least 30% without brute force actions, compared to the preceding 24 hours

The resource measured is CPU consumption of the real frozen Quantus node process.

The only attack-series measurement used to establish the selected impact is the final default-policy production-profile experiment.

It alternates ten times on the same long-lived node and CPU affinity:

1

Baseline window

Run a 45-second baseline window.

2

Partial handshake workload

Run a 4,000-connection partial /noise handshake workload using concurrency 400 and four-second holds.

For every pair:

Final default-policy result:

  • Mean: 39.3084%

  • Median: 39.6833%

  • Sample SD: 1.4905 percentage points

  • Student-t 95% CI: [38.2422%, 40.3745%]

  • Conservative lower bound: 38.2422% > 30%

The exact ten raw pairs are supplied as:

default-policy-paired.csv

The corresponding terminal output is supplied as:

default-policy-output.txt

The PoC does not claim that a literal 24-hour capture was performed. It measures the attack against repeated immediately preceding baselines on the same long-lived process. Because the workload is continuously recyclable, the measured steady-state delta can be maintained against a node whose preceding 24-hour workload is its normal baseline.

No stronger impact is claimed. This report does not claim node shutdown, network shutdown, mempool overflow, block stuffing, or unbounded memory growth.

References

  • Frozen in-scope asset: https://github.com/immunefi-team/audit-comp-quantus-chain/tree/3b243b870a5442a6e5f443056f67bbba87d472f5

  • PQ handshake implementation: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/client/litep2p/src/crypto/noise/mod.rs

  • Incoming connection-limit accounting: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/client/litep2p/src/transport/manager/limits.rs

  • Default P2P listener configuration: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/client/cli/src/params/network_params.rs

  • Quantus PR #574: https://github.com/Quantus-Network/chain/pull/574

  • Competition scope: https://immunefi.com/audit-competition/audit-comp-quantus/scope/

Proof of Concept

Scope and Safety

This PoC runs locally only against the frozen in-scope Quantus chain source at:

3b243b870a5442a6e5f443056f67bbba87d472f5

It starts the real Quantus node on 127.0.0.1 and does not contact public/mainnet/testnet infrastructure.

The PoC contains exactly three executable files:

  1. reproduce.sh

  2. poc/partial_handshake.py

  3. poc/paired_final.py

No separate attachment is required to understand or reproduce the finding: the full source of all three files and the measured results are included below.

The final authoritative reproduction uses:

  • the frozen SHA;

  • --profile production;

  • the default non-reserved P2P policy;

  • the real Quantus node;

  • 10 alternating baseline/attack pairs;

  • 45-second baseline windows;

  • 4,000 partial /noise handshakes per attack window;

  • concurrency 400;

  • four-second holds;

  • a Student-t 95% confidence interval;

  • a hard failure unless the lower 95% CI bound exceeds 30%.

The attacker sends only the canonical 28-byte multistream negotiation selecting /noise and then stops before providing a valid cryptographic handshake message.

1. reproduce.sh

Create reproduce.sh with the following contents:

Make it executable and run it:

The canonical checkout passed to the script must be clean and at:

A cold production-profile build may take a long time.

If a SHA-pinned production binary already exists, the same script supports a faster path:

A successful final run terminates with all of the following markers:

2. poc/partial_handshake.py

Create the directory:

Create poc/partial_handshake.py with the following contents:

This is the final noise-only client.

For every connection it sends only:

The encoded payload is 28 bytes:

The client then deliberately stops before sending a valid Noise/KEM cryptographic handshake message.

It does not contain the discarded development-only TCP-only or large-length/u16max modes.

3. poc/paired_final.py

Create poc/paired_final.py with the following contents:

This harness performs the actual impact measurement:

1

Measure the baseline

Measure the victim node's CPU rate for 45 seconds immediately before the attack.

2

Run partial /noise sessions

Open 4,000 partial /noise sessions at concurrency 400.

3

Hold sessions

Hold each partial session for four seconds.

4

Measure attack CPU rate

Measure the victim node's CPU rate during the attack window.

5

Calculate pair increase

Calculate the percentage increase for the pair.

6

Repeat the measurement

Repeat the baseline/attack sequence 10 times.

7

Calculate statistics

Calculate mean, median, sample standard deviation, and a two-sided Student-t 95% confidence interval.

8

Enforce threshold

Fail unless the lower 95% confidence bound is above 30%.

The percentage for each pair is calculated as:

4. Final Measured Result — Default Non-Reserved P2P Policy

This is the only attack-series result used to establish the selected Medium impact.

The final production-parity run used:

Measured result:

The conservative lower confidence bound exceeds the selected Medium threshold by:

The final submission does not rely on the older development-stage runs or on the earlier reserved-only configuration.

5. Same-Machine Historical Control

As a supplementary same-machine historical control, the preceding Quantus PQ transport at:

was built on the same machine using:

and tested with the same 28-byte protocol negotiation.

Measured result:

This control is supporting evidence only because it is a different commit and was built in release profile rather than the frozen target's production profile.

It is not used as the numerical oracle for the selected >=30% impact.

It provides evidence that generic TCP/multistream handling alone does not explain the frozen target's approximately 39% CPU increase.

6. Attacker/Victim CPU Cost Measurement

A separate production-profile cost experiment measured both attacker and victim CPU.

Measured output:

Therefore:

The attacker sends only:

during the attack window.

No ML-KEM key generation, ML-DSA signing, peer authentication, blockchain account, transaction, password, nonce search, or secret guessing is required from the attacker.

7. Pending-Handshake Concurrency Measurement

Fresh pending-handshake checks produced:

The final selected-impact workload uses only:

The test therefore remains below the successfully demonstrated 1,000-pending-probe level.

8. Why the Established Incoming-Connection Limit Does Not Prevent the Primitive

The relevant order is:

The expensive Quantus-specific PQ work is therefore triggered while the remote peer is still unauthenticated and before the connection consumes a slot in the established incoming-connection accounting.

9. Why This Is Not Brute Force

The PoC does not guess or search for:

  • a password;

  • a private key;

  • a peer identity;

  • a signature;

  • a nonce;

  • a hash preimage;

  • or any other secret/search-space value.

Each connection deterministically sends the same public 28-byte protocol negotiation and stops before authentication.

Repetition is used only to exercise the protocol-level pre-authentication resource asymmetry.

10. 24-Hour Comparator Statement

The selected program impact is:

Increasing network processing node resource consumption by at least 30% without brute force actions, compared to the preceding 24 hours

The PoC does not claim that a literal 24-hour capture was performed.

It measures the attack against repeated immediately preceding baselines on the same long-lived process. Because the workload is continuously recyclable, the measured steady-state delta can be maintained against a node whose preceding 24-hour workload is its normal baseline.

The final default-policy result is:

which remains materially above the 30% threshold.

11. Clean Reproduction Properties

The final reproduce.sh performs the following checks:

1

Verify frozen source

Verifies the canonical repository is at the frozen SHA.

Refuses a dirty canonical checkout.

Creates a detached temporary worktree.

2

Build and start the node

Builds the real Quantus node with --profile production unless a SHA-pinned binary is supplied.

Starts the victim on localhost.

Uses the default non-reserved P2P policy.

3

Run and validate the test

Runs the ten-pair statistical test.

Fails unless the lower 95% CI exceeds 30%.

Verifies canonical and detached source trees remain clean.

Emits explicit success markers.

Expected final success markers:

The primary mitigation is to bound pending handshakes before expensive PQ materialization.

12.1 Reserve pending-handshake capacity before NoiseContext::new()

Introduce a bounded pending-handshake semaphore/counter, optionally combined with a per-source/IP limit, and acquire it before the expensive per-connection PQ setup.

Conceptually:

The security-relevant ordering should become:

rather than:

12.2 Reduce or delay expensive work caused only by /noise selection

Where compatible with the intended PQ protocol, avoid performing all expensive local identity/signature work immediately after only an unauthenticated protocol selector.

If fresh per-connection KEM material is required, retain that property but perform it under the pending-handshake admission bound and delay additional expensive identity/signature materialization until the remote peer has supplied a structurally valid first cryptographic handshake message.

12.3 Keep connection-opening timeout as defense in depth

The existing opening timeout should remain, but it is not sufficient by itself because closed or expired partial sessions can be continuously replaced.

12.4 Add regression tests

Add tests/benchmarks proving that:

  • pending capacity is reserved before expensive PQ work;

  • the global pending cap is enforced;

  • optional per-source limits are enforced;

  • pending slots are released on success, parse error, remote close, and timeout;

  • established-connection accounting remains correct;

  • valid PQ handshakes continue to work; and

  • repeated partial /noise negotiations cannot reproduce the previous >=30% CPU increase outside the configured pending-handshake budget.

13. Final Reproduction Summary

The complete PoC above demonstrates the following chain using the real frozen production node:

The strongest evidence is therefore:

Was this helpful?