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

75034 bc medium unbounded brotli decompression in flashblock message decoding leads to node memory cpu exhaustion dos

Submitted on Apr 26th 2026 at 20:20:38 UTC by @raidocent for Audit Comp | Base Azul

  • Report ID: #75034

  • Report Type: Blockchain/DLT

  • Report severity: Medium

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

  • Impacts:

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

Description

Brief/Intro

The flashblocks decoder in base/base (crates/common/flashblocks) decompresses Brotli input with read_to_end and no limit on how large the output can grow. A small compressed message can expand to a huge buffer in memory before JSON is parsed. If that code path sees untrusted input and nothing else caps size first, it can be used for a decompression-style DoS (memory/CPU pressure on the process). No user funds are taken; it’s a liveness / resource issue.

Vulnerability Details

Where: Flashblock::try_parse_message in crates/common/flashblocks/src/block.rs (internal helper used by try_decode_message).

What’s wrong: If the bytes aren’t treated as plain JSON starting with {, the code uses the Brotli crate and does read_to_end into a Vec with no max decompressed length. The 4096 in Decompressor::new is only the read buffer size, not an output cap.

Relevant snippet (from 819ea306db40792a50626243034a62e3ca015ba6):

Fix direction: cap total decompressed bytes (e.g. take(n) on the reader) before read_to_end, and/or cap max wire length for the plain-JSON branch.

Impact Details

Impact I selected on the form: Increasing network processing node resource consumption by at least 30% without brute force actions, compared to the preceding 24 hours.

What an exploit would do: waste RAM and CPU on decode; could slow or crash individual nodes or services that parse these messages if they’re exposed to attacker-controlled payloads and don’t already limit size at the edge.

What I’m not claiming: chain split, stolen funds, bridge loss, or breaking L1 finality. I also can’t prove the exact 30% / 24h bar from a local test alone—that’s for you to judge against real deployment limits.

References

  • Code: https://github.com/base/base/blob/819ea306db40792a50626243034a62e3ca015ba6/crates/common/flashblocks/src/block.rs

  • Program: https://immunefi.com/audit-competition/audit-comp-base-azul/information/

  • Scope: https://immunefi.com/audit-competition/audit-comp-base-azul/scope/


Proof of Concept

This is written for triage and reproduction (per Step-by-step POCs for Audit Competitions). The bug is Rust in base/base crates/common/flashblocks, not a smart contract, so the Foundry / Solidity templates do not apply.

Rules: no mainnet or public testnets; reproduce with local cargo test only (not a live-network DoS). In-scope asset: github.com/base/base (use the program’s required ref, e.g. v0.8.0-rc.24).

1

The plain-JSON path only runs if from_utf8 succeeds and the text starts with {

The plain-JSON path only runs if from_utf8 succeeds and the text (after trim_start) starts with {. Otherwise the decoder takes the Brotli path and calls read_to_end into an unbounded Vec — no cap on decompressed size.

2

A small on-the-wire Brotli blob can still expand to a very large decompressed string

A small on-the-wire Brotli blob can still expand to a very large decompressed string, using RAM/CPU before further parsing.

How to verify: clone and check out the in-scope base/base ref. In crates/common/flashblocks/src/block.rs, add the new test and helpers below to the existing #[cfg(test)] mod tests (if that checkout already defines encode_brotli and sample_payload next to the other tests, add only decompress_brotli_expands_without_size_cap to avoid duplicate items). From the repo root, run: cargo test -p base-common-flashblocks decompress_brotli_expands_without_size_cap -- --ignored --nocapture


Vulnerable code (production, block.rs)

In impl Flashblock (module has use std::io::Read; and use bytes::Bytes; at top).

Reproduction test (same mod tests as other Flashblock tests)

The test encodes a JSON with a 1M-character pad field with Brotli, then decodes with try_decode_message (which uses try_parse_message). The compressed wire stays small; decompressed data is large.

Was this helpful?