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

75504 bc critical snappy decompression bomb in gossipsub message handler crashes base nodes

Submitted on Apr 29th 2026 at 14:21:53 UTC by @DeltaXV for Audit Comp | Base Azul

  • Report ID: #75504

  • Report Type: Blockchain/DLT

  • Report severity: Critical

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

  • Impacts:

    • RPC API crash affecting programs with greater than or equal to 25% of the market capitalization on top of the respective layer

    • Temporary freezing of network transactions by delaying one block by 500% or more of the average block time of the preceding 24 hours beyond standard difficulty adjustments

Description

Brief/Intro

base-consensus does not validate the declared decompressed size of snappy-compressed gossipsub messages before decompressing them. The OP implementation for instance checks and ensures DecodedLen <= maxGossipSize before decompression. With current implementation an attacker can send malformed p2p messages that validly decompresses to 191 MB, blocking the event loop crashing the P2P RPC and stalling the block processing for 30+ seconds.

Vulnerability Details

base-consensus does not validate the declared decompressed size of snappy-compressed gossipsub messages before decompressing them. The OP reference implementation checks DecodedLen <= maxGossipSize before decompression — Base omits this guard. An attacker can send a 9 MB gossip message that validly decompresses to 191 MB, blocking the event loop, stalling block delivery for 28+ seconds, and crashing the P2P RPC.

Every inbound gossipsub message passes through compute_message_id in config.rs, which calls snap::raw::Decoder::decompress_vec() unconditionally:

fn compute_message_id(msg: &Message) -> MessageId {
    let mut decoder = Decoder::new();
    let id = decoder.decompress_vec(&msg.data).map_or_else(
        |_| { /* hash raw data */ },
        |data| { /* hash decompressed data */ },
    );
    MessageId(id)
}

The snap crate (v1.1.1) reads a varint header from the compressed input declaring the output size, allocates a Vec<u8> of that size, then decompresses into it. Snappy achieves a consistent 21:1 compression ratio on identical-byte data, so 9 MB of compressed input produces 191 MB of valid decompressed output where every page is written to.

compute_message_id is registered as the message_id_fn on the gossipsub config (line 93). libp2p-gossipsub v0.49.4 calls it synchronously inside handle_received_message on the main event loop — there is no async yield point. The entire tokio task blocks while 191 MB is allocated, decompressed, and then SHA-256 hashed.

After compute_message_id returns, the message is delivered to the BlockHandler, which calls decode_v1decode_v4 in envelope.rs. These functions call decompress_vec a second time on the same data, doubling the work.

While the event loop is blocked processing bomb messages, legitimate gossip blocks from the sequencer cannot be received or forwarded to the engine. The P2P RPC endpoint, which shares the same tokio runtime, becomes unresponsive.

The OP reference implementation (op-node/p2p/gossip.go) guards both paths:

Message ID function (line 123):

Block validator (line 289):

Base omits both guards.

Impact Details

A single attacker can repeatedly crash the P2P RPC of any base-consensus node and delay block processing by 30+ seconds by sending crafted gossipsub messages from the mesh.

https://gist.github.com/DeltaXV/f08fdfcd01d934a6e2cbdfbb62030aa9

Proof of Concept

1

Start the devnet

2

Wait for all containers to be healthy

Create a snappy_bomb/monitor.sh file and paste the script from the gist. https://gist.github.com/DeltaXV/f08fdfcd01d934a6e2cbdfbb62030aa9

3

Run the monitor in a separate terminal

Create the snappy_bomb/src/main.rs file and paste the following code from the gist and do the same for snappy_bomb/cargo.toml: https://gist.github.com/DeltaXV/f08fdfcd01d934a6e2cbdfbb62030aa9

4

Build the PoC

5

Run the attack

5 concurrent peers, 5 messages each:

6

Observe the monitor

The unsafe head stalls for 28+ seconds and the RPC becomes unresponsive:

Here's a video reproducing the exploit -> https://mega.nz/file/if4zxSxC#C1HGlnYoWhm6Yri8fNG5etJD1-GiizmTD2hzLZjaNSU

Each attacker sends a valid gossipsub message containing 9 MB of snappy-compressed identical bytes. The decompression succeeds, the event loop stalls, and legitimate blocks cannot be processed.

Remediation

Add a decompress_len check before calling decompress_vec, matching the OP reference:

Apply the same guard to decode_v1 through decode_v4 in envelope.rs.

Was this helpful?