GitHub Advisoryadvisory · Go

GHSA-87m7-qffr-542v Klever-Go MultiDataInterceptor has remote OOM via crafted compressed P2P payload

HIGH Published 2026-05-13 Modified 2026-07-21 Official page ↗
Aliases
CVE-2026-44697
Ecosystem
Go
Packages
github.com/klever-io/klever-go
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H
Weaknesses
CWE-409, CWE-770
Reviewed
2026-05-13

Affected packages

• Go github.com/klever-io/klever-go: <= 1.7.16 (no fix yet)

Description

Summary A 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`: ```go if b.IsCompressed { err = b.Decompress(mdi.marshalizer) if err != nil { ... return err } } ``` `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: ```go func decompressGzip(data []byte) ([]byte, error) { rdata := bytes.NewReader(data) reader, err := gzip.NewReader(rdata) if err != nil { return nil, err } result, err := io.ReadAll(reader) // no LimitReader, no DataSize check ... } ``` 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`: ``` preProcessMessage -> anti-flood by COMPRESSED size only marshalizer.Unmarshal(&b, ..) -> outer Batch (small, cheap) b.Decompress(...) -> UNBOUNDED here (bomb explodes) ... b.Data populated with N entries ... antiflood.CanProcessMessagesOnTopic(..., uint32(len(b.Data)), ...) ``` 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 Concept The 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 source Place the file under `playground/p2pflood/c2_decompression_bomb_test.go` in a checkout of the reviewed commit, then run: ``` go test -v -count=1 -timeout=120s -run TestC2 ./playground/p2pflood/... ``` ```go package p2pflood_test import ( "bytes" "compress/gzip" "runtime" "sync/atomic" "testing" "time" "github.com/klever-io/klever-go/data/batch" "github.com/klever-io/klever-go/tools/marshal/factory" ) const inflatedSize = 256 heap)", amp) if allocated >100x", amp) } } // TestC2_DecompressionBomb_ValidInner: realistic ceiling — gzip stream // decompresses to a valid marshaled Batch with N=25M empty entries. // Decompress's internal Unmarshal succeeds and additionally allocates // the [][]byte slice. All before any count-based anti-flood runs. func TestC2_DecompressionBomb_ValidInner(t *testing.T) { mrs, err := factory.NewMarshalizer(factory.ProtoMarshalizer) if err != nil { t.Fatalf("ma…

Related

Alias · CVE (1)

  • CVE-2026-44697 Klever-Go is the Go implementation of the Klever blockchain protocol. vulnerability

Weakness · CWE (2)

  • CWE-409 Improper Handling of Highly Compressed Data (Data Amplification) weakness
  • CWE-770 Allocation of Resources Without Limits or Throttling weakness

References