Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-55734

CVE-2026-55734: Ueberauth Guardian DoS Vulnerability

CVE-2026-55734 is a denial of service flaw in Ueberauth Guardian that allows attackers to exhaust the BEAM atom table, crashing the entire node. This post covers technical details, affected versions, and mitigation.

Published:

CVE-2026-55734 Overview

CVE-2026-55734 is an uncontrolled resource allocation vulnerability [CWE-770] in the ueberauth/guardian authentication library for Elixir. The flaw resides in the Guardian.Permissions mixin, where encode_permissions!/1 calls String.to_atom/1 on attacker-controlled map keys before validating them against the configured permission set. Because BEAM atoms are never garbage collected and the atom table is a fixed-size resource (default ~1,048,576 entries), each unique key permanently consumes a slot. An attacker who can influence a permission map reaching this function can exhaust the atom table and crash the entire BEAM node, terminating every service running on it. The issue affects guardian from 2.0.0 before 2.4.1.

Critical Impact

Successful exploitation crashes the entire Erlang VM, taking down all applications and services hosted on the affected BEAM node.

Affected Products

  • Ueberauth Guardian 2.0.0 through 2.4.0
  • Elixir/Phoenix applications that call use Guardian.Permissions and pass user-controlled maps into encode_permissions!/1
  • Applications issuing tokens via encode_permissions_into_claims!/2 with request-derived permission data

Discovery Timeline

  • 2026-08-01 - CVE-2026-55734 published to NVD
  • 2026-08-06 - Last updated in NVD database

Technical Details for CVE-2026-55734

Vulnerability Analysis

The Guardian.Permissions mixin installs a public encode_permissions!/1 function on every module that invokes use Guardian.Permissions. For each key in the supplied map, the function calls String.to_atom(to_string(k)) before any validation runs. The integer-value clause of do_encode_permissions!/2 short-circuits directly to encoding without checking the key against the configured permission set, so an integer-valued entry with an unknown key is silently interned as a fresh atom.

Atoms in the BEAM virtual machine are never garbage collected. Once the atom table reaches its limit, the VM aborts with no more index entries in atom_tab, terminating every OTP application on that node. The sibling function decode_permissions/1 is unaffected because it skips keys absent from the configured permission set.

Root Cause

The root cause is unbounded atom creation from untrusted input. String.to_atom/1 is invoked before permission validation, and the integer-value code path never calls the validation logic in lib/guardian/permissions.ex. This combination allows an attacker to mint an unlimited number of unique atoms without ever triggering a PermissionNotFoundError.

Attack Vector

An attacker submits authentication or token-issuance requests containing a permissions map with attacker-chosen keys and integer values. When the application forwards this map to encode_permissions_into_claims!/2 or directly to encode_permissions!/1, each unique key permanently consumes an atom-table slot. Repeated requests exhaust the atom table and crash the node.

elixir
# Patch from lib/guardian/permissions.ex
 def encode_permissions!(map) when is_map(map) do
   for {k, v} <- map, into: %{} do
-    key = String.to_atom(to_string(k))
-    {key, do_encode_permissions!(v, k)}
+    type = to_string(k)
+
+    if Map.get(@normalized_perms, type) == nil do
+      raise PermissionNotFoundError, message: "#{to_string(__MODULE__)} - Type: #{type}"
+    end
+
+    {String.to_existing_atom(type), do_encode_permissions!(v, k)}
   end
 end

Source: GitHub commit 8d4efbfc. The fix validates the key against @normalized_perms first and then uses String.to_existing_atom/1, which raises rather than creating a new atom.

Detection Methods for CVE-2026-55734

Indicators of Compromise

  • Sudden growth in :erlang.system_info(:atom_count) approaching the :atom_limit value.
  • BEAM VM crashes with the error no more index entries in atom_tab (max=1048576) in system logs.
  • Repeated authentication or token-issuance requests carrying permission maps with high-entropy or randomized key names.

Detection Strategies

  • Instrument application telemetry to emit atom_count metrics on a regular interval and alert on sustained upward drift.
  • Log rejected PermissionNotFoundError exceptions after upgrading, and correlate the source IPs generating them.
  • Add web application firewall rules that flag request bodies containing large numbers of unrecognized permission keys.

Monitoring Recommendations

  • Track BEAM node restarts and unexpected supervisor tree terminations across the Elixir fleet.
  • Monitor request patterns targeting endpoints that call Guardian.encode_and_sign/3 with caller-supplied permission maps.
  • Ship BEAM crash dumps and Erlang error reports to a centralized log platform for review.

How to Mitigate CVE-2026-55734

Immediate Actions Required

  • Upgrade guardian to version 2.4.1 or later in mix.exs and run mix deps.update guardian.
  • Audit application code for callers that forward request-derived data into encode_permissions!/1 or encode_permissions_into_claims!/2.
  • Restart BEAM nodes after upgrading to reset any partially exhausted atom tables.

Patch Information

The fix landed in commit 8d4efbfc352d30f5fcfc75a4d69a795b0e472724 and shipped in Guardian 2.4.1. See the GitHub Security Advisory GHSA-9qx2-v587-q3gg and the CNA advisory from the Erlang Ecosystem Foundation for full remediation details.

Workarounds

  • Validate and whitelist permission-map keys against the configured permission set before invoking any Guardian encoding function.
  • Reject requests whose permission maps contain keys not defined in config :my_app, MyApp.Guardian, permissions: %{...}.
  • Increase the BEAM atom limit with +t as a temporary buffer, understanding this only delays exhaustion and is not a fix.
bash
# mix.exs - pin the fixed version
# {:guardian, "~> 2.4.1"}
mix deps.update guardian
mix deps.get

# Optional temporary hardening (not a substitute for patching)
# elixir --erl "+t 4194304" -S mix phx.server

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.