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

75301 bc critical attacker can dos consensus gossip via unbounded snappy decompression in message id computation

Submitted on Apr 28th 2026 at 12:05:11 UTC by @p_laksmana for Audit Comp | Base Azul

  • Report ID: #75301

  • Report Type: Blockchain/DLT

  • Report severity: Critical

  • Target: https://github.com/base/base/tree/v0.8.0-rc.28

  • Impacts:

    • Network not being able to confirm new transactions (total network shutdown)

Description

Brief/Intro

In the Base Azul consensus gossip implementation, the gossipsub message ID function decompresses inbound message data with Snappy before the message can be deduplicated or rejected by validation. The decompression call uses snap::raw::Decoder::decompress_vec, which allocates the full decompressed output without first checking the declared decompressed size.

An attacker who has been accepted as a normal Base Azul consensus P2P peer can publish Snappy-compressed payloads to the block gossipsub topic that are small enough to fit under the inbound gossip message-size cap, but expand into much larger buffers during message ID computation. This lets the attacker convert bandwidth into victim-side memory allocation and CPU work at approximately 21x amplification.

For example:

  1. The attacker creates a Snappy payload that is about 1.2 MB on the wire.

  2. The payload expands to about 25 MB when decompressed.

  3. The victim node receives the message and gossipsub invokes compute_message_id.

  4. compute_message_id calls decompress_vec(&msg.data) before any decompressed-size bound is enforced.

  5. A burst of 10 such messages causes about 250 MB of decompressed allocation from about 11 MB attacker input.

  6. On the local harness, this produced 236,044,288 bytes of RSS growth.

Vulnerability Details

The vulnerable code is registered in the production gossipsub config.crates/consensus/gossip/src/config.rs#L75-L93.

MAX_GOSSIP_SIZE limits the transmitted gossip message size, but it does not bound the output size of Snappy decompression inside the message ID function.crates/consensus/gossip/src/config.rs#L13-L16.

The actual bug is in compute_message_id. The function allocates the full decompressed buffer before hashing it into the message ID.crates/consensus/gossip/src/config.rs#L103-L122.

The important detail is that decompress_vec returns a newly allocated Vec<u8> containing the whole decompressed message. There is no call to snap::raw::decompress_len, no comparison against MAX_GOSSIP_SIZE, and no separate MAX_GOSSIP_DECOMPRESSED_SIZE guard before allocation.

This creates an attacker-controlled allocation path:

Since gossipsub invokes compute_message_id before block decoding or validation, Decoder::decompress_vec(&msg.data) expands the attacker-controlled Snappy payload and allocates the full decompressed buffer, triggering victim-side memory allocation and hashing work before the invalid payload can be rejected.

As a result, anyone accepted as a normal Base Azul consensus P2P peer can use small compressed gossip messages to consume disproportionate node memory and CPU, which can DoS consensus gossip processing.

Impact Details

This issue can cause consensus-gossip resource exhaustion. The affected component is Base-native consensus/offchain code, which is in scope for the Base Azul competition.

The direct impact is that an attacker can force victim nodes to allocate and hash decompressed payloads that are significantly larger than the bytes sent over the network. This can create substantial memory and CPU pressure on the victim node’s consensus-gossip path.

When the attacker repeatedly sends these compressed payloads, the victim node may experience delayed or exhausted consensus-gossip processing, resulting in node-level denial of service. If enough consensus-critical peers are affected concurrently, block gossip propagation may fail at the network level, potentially causing the network to be unable to confirm new transactions.

Attack Scenario

  1. The attacker runs a normal libp2p peer.

  2. The attacker connects to a Base Azul node that accepts peers on its consensus gossip interface.

  3. The attacker publishes Snappy-compressed bytes to /optimism/<l2_chain_id>/0/blocks.

  4. The payload does not need to decode as a valid block because message ID computation happens before block validation.

  5. Gossipsub invokes compute_message_id on the attacker-controlled payload.

  6. compute_message_id calls Decoder::decompress_vec(&msg.data).

  7. The victim allocates the full decompressed buffer before deduplication or validation can reject the message.

  8. The victim hashes the decompressed buffer to derive the message ID.

  9. Repeated P2P gossip messages create memory and CPU pressure proportional to the decompressed size, rather than the attacker’s compressed input size.

  10. As a result, the victim’s consensus-gossip processing can be delayed or exhausted, causing node-level DoS.

  11. If the attacker spams this attack and enough consensus peers are affected concurrently, block gossip propagation may fail network-wide, potentially causing the network to stop confirming new transactions.

References

Recommendation

  1. Do not call decompress_vec before checking the decompressed output length.

  2. Use snap::raw::decompress_len(&msg.data) or equivalent metadata parsing to read the declared decompressed size before allocation.

  3. Reject any message whose decompressed size exceeds MAX_GOSSIP_SIZE or a stricter MAX_GOSSIP_DECOMPRESSED_SIZE.

  4. Consider deriving MessageId from bounded data, such as a domain-separated hash of the compressed bytes plus the declared decompressed length, instead of hashing the full decompressed payload.

  5. Add per-peer and global rate limits for invalid or oversized Snappy messages.

  6. Add a regression test proving that a compressed payload declaring output larger than the limit is rejected before allocating the output buffer.

  7. Add a multi-node stress harness that measures block propagation and confirmation delay under concurrent Snappy-bomb gossip traffic.

Proof of Concept

  • Paste the PoC below into: /crates/consensus/gossip/tests/poc_gossipsub_snappy_bombs.rs

  • Run with cargo test -p base-consensus-gossip --test poc_gossipsub_snappy_bombs -- --nocapture --test-threads

Output:

POC:

Was this helpful?