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.
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.
Rules: no mainnet or public testnets; reproduce with localcargo 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 onlydecompress_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.