GHSA-87m7-qffr-542v: Klever-Go MultiDataInterceptor has remote OOM via crafted compressed P2P payload
SummaryA remote, unauthenticated denial-of-service vulnerability in
`Batch.Decompress` (`data/batch/batch.go`) allows any peer that
participates in a topic served by `MultiDataInterceptor` to allocate
multi-gigabyte heaps on the receiving node from a sub-50 KiB gossip
payload. A single packet is sufficient to OOM-kill a validator with
conventional memory provisioning. Fleet-wide application affects chain
liveness.The vulnerability was identified during an internal security review of
`core/process/interceptors/multiDataInterceptor.go` at commit
`405d01b0abbf0d3e73b4a990bd7394a01f200dc2`. It is distinct from, and
substantially more severe than, the throttler-slot-leak vulnerability
disclosed in `GHSA-74m6-4hjp-7226`. Both reports cover adjacent code in
the same call path; the patches must land together in one release
(rc2 superseding rc1).Two additional, lower-severity hardening issues affecting the same code
path are documented in this report and remediated by the same patch.
They are not independently exploitable under the default deployed
anti-flood configuration and are not requested as separate CVEs.Description`MultiDataInterceptor.ProcessReceivedMessage`
(`core/process/interceptors/multiDataInterceptor.go:79`) handles every
gossip message received on the topics the interceptor is registered for.
At lines 95–102 it conditionally decompresses the payload via
`Batch.Decompress`:`Batch.Decompress` (`data/batch/batch.go:109`) delegates the gzip step to
`decompressGzip` (`data/batch/batch.go:35-53`), which performs an
unbounded `io.ReadAll` on the gzip reader:After the gzip step succeeds, `Decompress` re-`Unmarshal`s the inflated
bytes back into the `Batch` value, again with no size cap. The
attacker-set `ba.DataSize` field is never validated on decompression, so
the lie is free.The order of operations in `ProcessReceivedMessage`:The count-budget anti-flood check at line 111 runs *after* `Decompress`
completes, so no anti-flood configuration can prevent the explosion. The
only gate above `Decompress` is `preProcessMessage`'s byte budget, which
sees only the *compressed* payload size and is trivially satisfied by a
sub-MB bomb.Proof of ConceptThe PoC is a self-contained Go test that exercises the real
`data/batch.Batch.Decompress` function and the production
`factory.ProtoMarshalizer`. No mocks. Both the attacker-side construction
(marshal a `Batch` of millions of empty entries, gzip, wrap in an outer
compressed `Batch`) and the receiver-side path (`mrs.Unmarshal` → 
`received.Decompress(mrs)`) are exactly what runs in production at the
reviewed commit.The headline test (`TestC2_DecompressionBomb_ValidInner`) constructs a
~48 KiB outer wire payload that decompresses to 25 million `[]byte`
entries, and samples `runtime.HeapAlloc` every 5 ms during `Decompress`
to capture the peak (since the inflated buffer is freed once `Decompress`
returns).Test sourcePlace the file under `playground/p2pflood/c2_decompression_bomb_test.go`
in a checkout of the reviewed commit, then run:Measured outputApple-silicon dev machine, `go 1.25`, against commit
`405d01b0abbf0d3e73b4a990bd7394a01f200dc2`:Reproduction: any commit that includes `data/batch/batch.go` in its
current `decompressGzip`/`Decompress` form. The PoC does not depend on
libp2p, the live interceptor stack, or any deployed configuration — the
bug is in `Batch.Decompress` itself; any caller that reaches it pays
for the unbounded allocation.The PoC sources (along with a companion test for the bundled
slice-prealloc finding) live under `playground/p2pflood/` on the
maintainer's local workstation and have not been pushed to any branch.
They will be converted into a regression-test suite alongside the patch
in the private fork.ImpactA single connected peer publishing on a topic served by
`MultiDataInterceptor` (which on a public chain includes any anonymous
gossip publisher) can cause the receiving node to allocate 2+ GiB of
heap in under one second per packet.With the default deployed configuration
(`peerMaxInput.totalSizePerInterval: 4194304` = 4 MiB/s per peer), an
attacker can ship roughly 80 such bombs per second per connected peer
before tripping the per-peer byte budget. The per-peer message count
limit (`baseMessagesPerInterval: 140` per fastReacting interval, 1000
before blacklisting) is high enough to permit the attack to run for
several seconds before any blacklist activates. By that point the node
process is already OOM-killed.Realistic attack scenarios:A single attacker connected to one validator can OOM that validator
in under a second (one bomb suffices on memory-constrained nodes).A small number of malicious peers spread across the validator fleet
can OOM the entire fleet within a single block-production interval,
affecting chain liveness.Eclipse-attack composition: the cost is paid before any peer
reputation logic runs, so the attack works regardless of whether the
receiver attributes the message to originator or relayer.Affected Code`data/batch/batch.go:35-53`   — `decompressGzip`, unbounded `io.ReadAll``data/batch/batch.go:109-137` — `Batch.Decompress`, ignores `DataSize`,
                                 re-`Unmarshal`s inflated bytes`core/process/interceptors/multiDataInterceptor.go:95-102` — call site`core/process/interceptors/multiDataInterceptor.go:84-94`  — preceding
                                 `Unmarshal` stepPatchesA patch is in preparation on a private branch and will land in rc2,
together with the fix for `GHSA-74m6-4hjp-7226`. The intended fix
shape:The cap value should be selected per topic. A 64 MiB ceiling preserves
backward compatibility for legitimate large batches while reducing the
worst-case allocation by ≈30× relative to the measured PoC and ≈400×
relative to the upper bound of an uncapped attack.A regression test based on the PoC will accompany the patch.WorkaroundsNone at the configuration level. The `peerMaxInput.totalSizePerInterval`
budget could theoretically be lowered, but as the PoC measurements show,
a single bomb is already lethal on memory-constrained nodes. Patch is
required.Bundled Hardening (no separate CVE)The following two issues were identified in the same call path during
the review. They are not independently exploitable under the default
deployed `defaultMaxMessagesPerSec: 35000` per-topic anti-flood limit
and so do not warrant their own CVEs. They are remediated by the same
patch as the headline vulnerability and are documented here for
transparency.Bundled #1 — Slice pre-allocation amplification (CWE-789, CWE-770)`multiDataInterceptor.go:123` performs:`len(multiDataBuff)` is `len(b.Data)` after `Unmarshal` and `Decompress`,
both of which are attacker-controlled. Under the default per-topic
count budget this is bounded; a deployer who loosens that budget, or
any future code path that bypasses it, would expose ≈16 bytes ×
attacker-chosen-N of allocation. The same patch caps `len(b.Data)`
immediately after `Unmarshal`, again after `Decompress`, and before the
make.The unconditional component of this finding — that `Decompress`'s
internal `Unmarshal` populates `b.Data` with N `[]byte` slice headers
(24 B each) before any count-budget check runs — is captured by the
headline finding's PoC.Bundled #2 — Self-message anti-flood bypass (CWE-290, CWE-693)`baseDataInterceptor.go:32` exempts messages from anti-flood enforcement
when:The first equality is a sentinel byte comparison, not a cryptographic
check. Exploitability depends on whether the upstream libp2p stack
verifies envelope signatures before reaching `preProcessMessage`. The
patch replaces the sentinel with a defense-in-depth check and ensures
throttler accounting still runs on the self-message path.Coordination with `GHSA-74m6-4hjp-7226`The maintainer team is concurrently handling `GHSA-74m6-4hjp-7226`,
which discloses an adjacent throttler-slot-leak finding in the same
`ProcessReceivedMessage` function. The two CVEs are independently
fixable per CNA Operational Rules, but operationally the patches must
land in one release. rc2 will supersede rc1 and contain fixes for both
advisories. Validators upgrade once.CreditsFernando Sobreira (maintainer, internal security review).ReferencesReviewed commit: `405d01b0abbf0d3e73b4a990bd7394a01f200dc2`Related advisory: `GHSA-74m6-4hjp-7226`CWE-409: https://cwe.mitre.org/data/definitions/409.htmlCWE-770: https://cwe.mitre.org/data/definitions/770.html
CVSS v3.1: 8.6