VYPR
High severityNVD Advisory· Published Jun 15, 2026

CVE-2026-53430

CVE-2026-53430

Description

The Elixir gRPC library before 1.0.0 allows unauthenticated remote attackers to crash servers via a gzip decompression bomb.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

The Elixir gRPC library before 1.0.0 allows unauthenticated remote attackers to crash servers via a gzip decompression bomb.

Vulnerability

CVE-2026-53430 is a denial of service vulnerability in the elixir-grpc grpc library (versions 0.4.0 up to, but not including, 1.0.0). The issue resides in GRPC.Compressor.Gzip.decompress/1 (file lib/grpc/compressor/gzip.ex) and GRPC.Message.from_data/2 (lib/grpc/message.ex). The decompress/1 function calls :zlib.gunzip/1 directly on attacker-controlled bytes without any decompressed-size limit, compression ratio check, or incremental decoding [1][2]. Because this module is registered as the gzip compressor for the gRPC framework, it is automatically invoked whenever an incoming gRPC frame carries the grpc-encoding: gzip header. The server's max_receive_message_length is enforced only against the already-decompressed message, so it provides no protection against this amplification [1].

Exploitation

An unauthenticated remote attacker simply needs network access to the gRPC endpoint and the ability to send a single crafted gRPC frame. The attacker constructs a highly compressible payload (e.g., a few kilobytes of zeros, which gzip compresses at roughly 1000:1) [1][3]. Sending this frame with grpc-encoding: gzip triggers the vulnerable decompression routine. :zlib.gunzip/1 allocates the entire decompressed result as a single binary, expanding the small payload to multiple gigabytes in a single function call [1]. No user interaction, authentication, or prior state is required.

Impact

Successful exploitation exhausts the BEAM node's heap, causing an out-of-memory (OOM) kill and resulting in a denial of service (DoS) for the gRPC server [1][2][3]. The impact is limited to availability; no data confidentiality or integrity is compromised. The CVSS v4.0 score is 8.7 (High) with vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N [3].

Mitigation

The vulnerability is fixed in version 1.0.0 of the grpc package [1][2]. The fix applies incremental decompression with chunked input (8 KB slices) and enforces a configurable max_decompressed_message_length (default 4 MB) [4]. Any service using the library should upgrade to version 1.0.0 or later. For those unable to upgrade immediately, disabling gzip compression on the server side or applying network-layer filtering to drop suspiciously small compressed frames may temporarily reduce risk, but no official workaround is provided in the reference advisories [1].

AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Elixir Grpc/Grpcreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: >=0.4.0, <1.0.0

Patches

1
1afbab9d57d2

fix: mitigate zip bomb (#543)

https://github.com/elixir-grpc/grpcPaulo ValenteJun 15, 2026via body-scan
2 files changed · +92 1
  • grpc_core/lib/grpc/compressor/gzip.ex+42 1 modified
    @@ -1,6 +1,14 @@
     defmodule GRPC.Compressor.Gzip do
       @behaviour GRPC.Compressor
     
    +  # 4 MB – matches gRPC-Go's default max receive message size.
    +  # Override with: Application.put_env(:grpc, :max_decompressed_message_length, bytes)
    +  @default_max_decompressed_size 4 * 1024 * 1024
    +
    +  # Feed compressed input in 8 KB slices so we can detect an oversized output
    +  # before allocating the full decompressed payload.
    +  @input_chunk_size 8_192
    +
       def name do
         "gzip"
       end
    @@ -10,6 +18,39 @@ defmodule GRPC.Compressor.Gzip do
       end
     
       def decompress(data) do
    -    :zlib.gunzip(data)
    +    max_size =
    +      Application.get_env(:grpc, :max_decompressed_message_length, @default_max_decompressed_size)
    +
    +    z = :zlib.open()
    +    # windowBits 31 = 15 (max window) + 16 (gzip format flag)
    +    :ok = :zlib.inflateInit(z, 31)
    +
    +    try do
    +      chunks = inflate_chunks(z, data, max_size, 0, [])
    +      :zlib.inflateEnd(z)
    +      IO.iodata_to_binary(chunks)
    +    after
    +      :zlib.close(z)
    +    end
       end
    +
    +  defp inflate_chunks(_z, <<>>, _max_size, _acc_size, acc), do: acc
    +
    +  defp inflate_chunks(z, data, max_size, acc_size, acc) do
    +    {chunk, rest} = split_chunk(data)
    +    output = :zlib.inflate(z, chunk)
    +    new_size = acc_size + IO.iodata_length(output)
    +
    +    if new_size > max_size do
    +      raise GRPC.RPCError,
    +        status: :resource_exhausted,
    +        message: "Decompressed message exceeds limit of #{max_size} bytes"
    +    end
    +
    +    inflate_chunks(z, rest, max_size, new_size, [acc, output])
    +  end
    +
    +  defp split_chunk(data) when byte_size(data) <= @input_chunk_size, do: {data, <<>>}
    +
    +  defp split_chunk(<<chunk::bytes-size(@input_chunk_size), rest::binary>>), do: {chunk, rest}
     end
    
  • grpc_core/test/grpc/compressor/gzip_test.exs+50 0 added
    @@ -0,0 +1,50 @@
    +defmodule GRPC.Compressor.GzipTest do
    +  use ExUnit.Case, async: true
    +
    +  alias GRPC.Compressor.Gzip
    +
    +  test "round-trips small payloads correctly" do
    +    data = String.duplicate("hello world ", 50)
    +    assert Gzip.decompress(Gzip.compress(data)) == data
    +  end
    +
    +  test "raises GRPC.RPCError :resource_exhausted on decompression bomb" do
    +    # Build a small gzip payload that decompresses to 2x the default 4 MB limit.
    +    bomb_size = 9 * 1024 * 1024
    +    compressed = :zlib.gzip(:binary.copy(<<0>>, bomb_size))
    +
    +    assert_raise GRPC.RPCError, ~r/exceeds limit/, fn ->
    +      Gzip.decompress(compressed)
    +    end
    +  end
    +
    +  test "respects :max_decompressed_message_length application env" do
    +    # Temporarily lower the limit to 1 KB.
    +    Application.put_env(:grpc, :max_decompressed_message_length, 1024)
    +
    +    on_exit(fn ->
    +      Application.delete_env(:grpc, :max_decompressed_message_length)
    +    end)
    +
    +    oversized = :zlib.gzip(:binary.copy(<<0>>, 2048))
    +
    +    assert_raise GRPC.RPCError, ~r/exceeds limit/, fn ->
    +      Gzip.decompress(oversized)
    +    end
    +  end
    +
    +  test "accepts payload exactly at the configured limit" do
    +    limit = 4096
    +    Application.put_env(:grpc, :max_decompressed_message_length, limit)
    +
    +    on_exit(fn ->
    +      Application.delete_env(:grpc, :max_decompressed_message_length)
    +    end)
    +
    +    # Use incompressible data so the decompressed size is exactly `limit`.
    +    data = :crypto.strong_rand_bytes(limit)
    +    compressed = :zlib.gzip(data)
    +
    +    assert Gzip.decompress(compressed) == data
    +  end
    +end
    

Vulnerability mechanics

Root cause

"Missing decompressed-size limit, ratio check, and incremental decoding in `GRPC.Compressor.Gzip.decompress/1` allows a gzip decompression bomb to exhaust server memory."

Attack vector

An unauthenticated remote attacker sends a single crafted gRPC frame with the `grpc-encoding: gzip` header and a small, highly compressible payload (e.g., a few kilobytes of zeros that gzip compresses at roughly 1000:1) [ref_id=1]. The server's `GRPC.Compressor.Gzip.decompress/1` calls `:zlib.gunzip/1`, which allocates the entire decompressed result as a single binary, expanding the payload to multiple gigabytes and exhausting the BEAM node's heap, triggering an out-of-memory kill [ref_id=1]. No authentication, prior state, or special configuration is required — the attacker only needs to reach the gRPC port [ref_id=1].

Affected code

The vulnerability resides in `lib/grpc/compressor/gzip.ex` in the `GRPC.Compressor.Gzip.decompress/1` function, which called `:zlib.gunzip/1` directly on attacker-controlled bytes with no size limit, ratio check, or incremental decoding [ref_id=1]. Because this module is the registered gzip `GRPC.Compressor` implementation, it is invoked automatically whenever an incoming gRPC frame carries the `grpc-encoding: gzip` header. The `GRPC.Message.from_data/2` function in `lib/grpc/message.ex` is the entry point that triggers the vulnerable decompression path [ref_id=1].

What the fix does

The patch replaces the single `:zlib.gunzip/1` call with incremental decompression using `:zlib.inflate/2` fed in 8 KB chunks [patch_id=6113715]. After each chunk is decompressed, the accumulated output size is checked against a configurable `max_decompressed_message_length` (defaulting to 4 MB) [patch_id=6113715]. If the limit is exceeded, a `GRPC.RPCError` with status `:resource_exhausted` is raised, preventing the full decompression bomb from being allocated [patch_id=6113715]. This ensures the server detects oversized decompressed data before exhausting memory.

Preconditions

  • networkThe attacker must be able to reach the gRPC port of a server built on this library.
  • configThe server must accept gzip-compressed requests (default behavior when the compressor is registered).

Generated on Jun 16, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.