Elixir protobuf, Denial of Service (Unbounded Recursion), GHSA-rv48-qqj5-crxg (High) -DC-Jul2026-1034

Listen to this Post

How the mentioned CVE works

The vulnerability exists in the `Protobuf.Decoder` module of the Hex `protobuf` package for Elixir. The decoder lacks a recursion-depth limit when processing nested, embedded message fields. This oversight affects all versions from 0.8.0 up to, but not including, 0.16.1.
The flawed logic resides in Protobuf.Decoder.value_for_field/3, specifically in the `embedded?: true` branch at lib/protobuf/decoder.ex:218-243. When the decoder encounters an embedded message field, it makes a recursive call to decode(bin, type). This call re-enters the decoding loop through build_message → handle_value → value_for_field. Critically, this recursive call is not in tail position; the result is consumed by the surrounding `decode` after it returns. As a result, every level of nesting retains a live frame on both the process stack and heap.
An attacker can exploit this by crafting a Protobuf message that uses a self-referential schema (e.g., message Tree { Tree child = 1; }) or any cyclic message type. By nesting this embedded field hundreds of thousands to millions of levels deep, the attacker forces the BEAM VM to recurse once per level. Each additional level of nesting in the payload requires only a 1-byte field tag plus a varint length prefix, meaning the depth grows roughly inversely with the payload size. A small request body of just a few kilobytes to a few megabytes can therefore trigger an enormous recursion depth.
The recursion continues until the process exhausts its memory, blows the stack, or starves the scheduler while performing garbage collection over the deeply nested structure. A handful of such concurrent requests can take the entire node offline in a request-amplification denial-of-service attack. This is in stark contrast to reference implementations (Google’s C++, Java, etc.) which cap recursion at 100 levels precisely to prevent this scenario.
The fix, implemented in version 0.16.1, threads a depth counter through the decode functions and raises `Protobuf.DecodeError` once the configurable limit (defaulting to 100) is exceeded.

DailyCVE Form

Platform: Elixir/BEAM
Version: >=0.8.0, <0.16.1
Vulnerability: Unbounded Recursion
Severity: High
Date: 2026-05-12

Prediction: Already Patched (2026-05-12)

What Undercode Say: Analytics

To determine if your application is vulnerable, audit your dependencies for the `protobuf` package.

Check your mix.lock for the protobuf version
grep -A 1 "protobuf" mix.lock
Example output for a vulnerable version
"protobuf": {:hex, :protobuf, "0.15.0", ...}
Example output for a patched version
"protobuf": {:hex, :protobuf, "0.16.1", ...}

You can also check the version directly in your application’s `mix.exs` file.

In mix.exs
defp deps do
[
{:protobuf, "~> 0.15.0"} Vulnerable
{:protobuf, "~> 0.16.1"} Patched
]
end

Exploit: Proof of Concept

The following steps outline how an attacker could craft a malicious payload to trigger the vulnerability.
1. Define a Self-Referential Schema: The attacker defines a Protobuf schema with a self-referential field.

defmodule Tree do
use Protobuf, syntax: :proto3
field(:child, 1, type: Tree)
end

2. Build the Malicious Payload: The attacker builds a wire-format body from the inside out. At each of `depth` levels, they prepend `<<0x0A, length_varint(inner_size), inner>>` (where tag `0x0A` represents field 1, wire type 2). An iolist with a running byte-size is used to keep generation time linear O(depth).
3. Launch the Attack: The attacker POSTs the body (e.g., a few MB at depth = 1_000_000) as `application/x-protobuf` to any endpoint that calls Tree.decode/1.
4. Observe the Impact: The non-tail `decode(bin, type)` in `value_for_field/3` re-enters once per nesting level, accumulating a frame per level. The decode operation consumes seconds of CPU and hundreds of MB of memory on the victim node; a few concurrent requests are enough to exhaust the node’s resources.

Protection

The primary protection is to upgrade the `protobuf` package to version 0.16.1 or later. This version introduces a recursion limit that defaults to 100, matching the limit in reference implementations.

In mix.exs
defp deps do
[
{:protobuf, "~> 0.16.1"}
]
end

After updating, run `mix deps.get` to fetch the new version.
For applications that cannot be immediately updated, consider these mitigations:
Input Validation: If possible, validate and reject messages that appear to have excessive nesting before passing them to the decoder.
Network Defenses: Deploy a reverse proxy or Web Application Firewall (WAF) that can detect and block requests with unusually deep nesting patterns, although this is a less reliable mitigation.
Resource Limits: Implement strict request size limits and concurrency controls to reduce the impact of a single malicious request.

Impact

  • Unauthenticated, network-reachable request-amplification denial of service.
  • A single small request can consume seconds of CPU and hundreds of MB of memory on the victim.
  • A few concurrent requests can take the entire node offline.
  • Any service that decodes attacker-influenced protobuf bytes into a self-referential or cyclic message type is affected.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top