CVE-2026-32688 Overview
A resource exhaustion vulnerability exists in elixir-plug plug_cowboy versions 2.0.0 through 2.8.0 that allows unauthenticated remote attackers to trigger a denial of service condition. The vulnerability stems from improper handling of the HTTP/2 :scheme pseudo-header, where attacker-controlled values are passed directly to String.to_atom/1, leading to BEAM atom table exhaustion and complete node failure.
The Plug.Cowboy.Conn.conn/1 function in lib/plug/cowboy/conn.ex converts the scheme value returned by :cowboy_req.scheme/1 into an atom without validation. When processing HTTP/2 connections, the cowlib library passes the client-supplied :scheme pseudo-header value through verbatim. Since atoms in the Erlang VM are never garbage-collected and the atom table has a fixed limit (default 1,048,576 entries), an attacker can exhaust this table by sending HTTP/2 requests with unique scheme values. Once exhausted, the Erlang VM aborts with a system_limit error, taking down the entire node.
Critical Impact
Unauthenticated remote attackers can crash the entire Erlang VM by exhausting the atom table through HTTP/2 requests with unique scheme values, causing complete service unavailability.
Affected Products
- plug_cowboy versions 2.0.0 through 2.8.0
- Applications using HTTP/2 with affected plug_cowboy versions
- Elixir/Phoenix applications deployed with vulnerable plug_cowboy dependencies
Discovery Timeline
- 2026-04-27 - CVE-2026-32688 published to NVD
- 2026-04-27 - Last updated in NVD database
Technical Details for CVE-2026-32688
Vulnerability Analysis
This vulnerability exploits a fundamental characteristic of the Erlang BEAM virtual machine: atoms are immutable, stored in a fixed-size table, and are never garbage collected during the lifetime of the VM. The plug_cowboy library converts user-controlled HTTP/2 scheme values to atoms without validation, creating an attack surface for resource exhaustion.
In HTTP/2 protocol, the :scheme pseudo-header is mandatory and typically contains "http" or "https". However, the cowlib library does not validate this value before passing it to the Plug adapter. When Plug.Cowboy.Conn.conn/1 calls String.to_atom/1 on each unique scheme value, a new permanent atom is allocated. By sending approximately one million HTTP/2 requests with unique scheme values, an attacker can fill the atom table completely, triggering a system_limit exception that crashes the entire Erlang VM.
This attack is particularly dangerous because it affects the entire node, not just individual connections or processes. HTTP/1.1 connections are not affected because cowboy derives the scheme from the listener configuration rather than from client-supplied headers.
Root Cause
The root cause is the unconditional use of String.to_atom/1 on untrusted, user-controlled input from HTTP/2 pseudo-headers. The cowlib library does not validate the :scheme pseudo-header against expected values ("http" or "https"), and the plug_cowboy adapter assumes the value is safe to convert to an atom. This violates the security principle of never converting user input to atoms in Erlang/Elixir applications due to the finite and permanent nature of the atom table.
Attack Vector
The attack leverages the network-accessible HTTP/2 endpoint with no authentication requirements. An attacker sends a large volume of HTTP/2 requests, each containing a unique :scheme pseudo-header value (e.g., "scheme-00001", "scheme-00002", etc.). Since each unique string creates a permanent atom entry, the attacker can systematically exhaust the atom table. The attack requires no special privileges and can be automated to reach the default limit of 1,048,576 atoms relatively quickly, depending on network conditions and server capacity.
The security patch addresses this vulnerability by pattern matching only on the expected "http" and "https" scheme values, returning the corresponding atoms :http and :https respectively:
peer: {remote_ip, _}
} = req
+ scheme =
+ case :cowboy_req.scheme(req) do
+ "http" -> :http
+ "https" -> :https
+ end
+
%Plug.Conn{
adapter: {__MODULE__, Map.put(req, :plug_pid, self())},
host: host,
Source: GitHub Commit Update
This fix ensures that only the two predefined atoms (:http and :https) are ever used, preventing the creation of new atoms from arbitrary user input. Requests with invalid scheme values will now fail to match, resulting in a function clause error rather than atom table exhaustion.
Detection Methods for CVE-2026-32688
Indicators of Compromise
- Unusual growth in BEAM atom table usage, detectable via :erlang.system_info(:atom_count) or monitoring tools
- High volume of HTTP/2 requests with non-standard :scheme pseudo-header values (anything other than "http" or "https")
- Erlang VM crashes with system_limit errors related to atom table exhaustion
- Application logs showing pattern match failures after patching if attackers continue attempts
Detection Strategies
- Monitor atom table usage metrics using :erlang.system_info(:atom_count) and alert when approaching the limit (default 1,048,576)
- Implement HTTP/2 request logging that captures :scheme pseudo-header values to identify anomalous patterns
- Deploy intrusion detection rules to flag high-frequency HTTP/2 requests from single sources with varying scheme values
- Use SentinelOne Singularity to monitor for process crashes and service disruptions indicative of resource exhaustion attacks
Monitoring Recommendations
- Set up alerts for atom count exceeding 80% of the configured maximum limit
- Monitor application uptime and implement automatic restart mechanisms with exponential backoff
- Track HTTP/2 connection patterns and implement rate limiting for connections exhibiting suspicious behavior
- Review BEAM VM crash dumps for system_limit errors referencing atom allocation
How to Mitigate CVE-2026-32688
Immediate Actions Required
- Upgrade plug_cowboy to version 2.8.1 or later immediately
- If immediate upgrade is not possible, consider temporarily disabling HTTP/2 support and using HTTP/1.1 only
- Review application dependencies using mix deps.tree to identify all projects using vulnerable plug_cowboy versions
- Monitor atom table usage closely until patches are applied
Patch Information
The vulnerability is fixed in plug_cowboy version 2.8.1. The fix modifies Plug.Cowboy.Conn.conn/1 in lib/plug/cowboy/conn.ex to use pattern matching against the known valid schemes "http" and "https" instead of dynamically converting user input to atoms. For detailed patch information, see the GitHub Security Advisory GHSA-q8x4-x7mp-5vg2 and the security patch commit.
Workarounds
- Temporarily disable HTTP/2 support by configuring your endpoint to use only HTTP/1.1 listeners
- Implement a reverse proxy or load balancer that validates HTTP/2 scheme headers before forwarding requests
- Increase the atom table limit using +t flag (e.g., +t 2097152) as a temporary measure to extend time before exhaustion, though this does not fix the underlying vulnerability
- Deploy rate limiting at the network edge to reduce the speed at which an attacker can exhaust resources
# Configuration example: Temporarily disable HTTP/2 in Phoenix endpoint
# In config/prod.exs, configure the endpoint to use only HTTP/1.1:
config :my_app, MyAppWeb.Endpoint,
http: [port: 4000, protocol_options: [idle_timeout: 60000]],
# Remove or comment out https with http2 options:
# https: [port: 443, cipher_suite: :strong, certfile: "...", keyfile: "..."]
# To increase atom table limit (temporary mitigation only):
# Add to vm.args or release configuration:
# +t 2097152
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

