CVE-2026-34593
HIGHSeverity by source
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
Ash.Type.Module.cast_input/2 unconditionally creates a new Erlang atom via Module.concat([value]) for any user-supplied binary string that starts with "Elixir.", before verifying whether the referenced module exists. Because Erlang atoms are never garbage-collected and the BEAM atom table has a hard default limit of approximately 1,048,576 entries, an attacker who can submit values to any resource attribute or argument of type :module can exhaust this table and crash the entire BEAM VM, taking down the application.
Details
Setup: A resource with a :module-typed attribute exposed to user input, which is a supported and documented usage of the Ash.Type.Module built-in type:
defmodule MyApp.Widget do
use Ash.Resource, domain: MyApp, data_layer: AshPostgres.DataLayer
attributes do
uuid_primary_key :id
attribute :handler_module, :module, public?: true
end
actions do
defaults [:read, :destroy]
create :create do
accept [:handler_module]
end
end
endVulnerable code in lib/ash/type/module.ex, lines 105-113:
def cast_input("Elixir." <> _ = value, _) do
module = Module.concat([value])
# <-- Creates new atom unconditionally
if Code.ensure_loaded?(module) do
{:ok, module}
else
:error
# <-- Returns error but atom is already created
end
endExploit: Submit repeated Ash.create requests (e.g., via a JSON API endpoint) with unique "Elixir.*" strings:
# Attacker-controlled loop (or HTTP requests to an API endpoint)
for i <- 1..1_100_000 do
Ash.Changeset.for_create(MyApp.Widget, :create, %{handler_module: "Elixir.Attack#{i}"})
|> Ash.create()
# Each iteration: Module.concat(["Elixir.Attack#{i}"]) creates a new atom
# cast_input returns :error but the atom :"Elixir.Attack#{i}" persists
end
# After ~1,048,576 unique strings: BEAM crashes with system_limitContrast: The non-"Elixir." path in the same function correctly uses String.to_existing_atom/1, which is safe because it only looks up atoms that already exist:
def cast_input(value, _) when is_binary(value) do
atom = String.to_existing_atom(value)
# safe - raises if atom doesn't exist
...
endAdditional occurrence: cast_stored/2 at line 141 contains the identical pattern, which is reachable when reading :module-typed values from the database if an attacker can write arbitrary "Elixir.*" strings to the relevant database column.
Impact
An attacker who can submit requests to any API endpoint backed by an Ash resource with a :module-typed attribute or argument can crash the entire BEAM VM process. This is a complete denial of service: all resources served by that VM instance (not just the targeted resource) become unavailable. The crash cannot be prevented once the atom table is full, and recovery requires a full process restart.
Fix direction: Replace Module.concat([value]) with String.to_existing_atom(value) wrapped in a rescue ArgumentError block (as already done in the non-"Elixir." branch), or validate that the atom already exists before calling Module.concat by first attempting String.to_existing_atom and only falling back to Module.concat on success.
AnalysisAI
BEAM VM atom table exhaustion in Ash Framework's Module type allows remote denial-of-service against Elixir applications. The ash package (all versions prior to v3.22.0) unconditionally creates Erlang atoms from user-supplied strings in Ash.Type.Module.cast_input/2 before validation, enabling attackers to crash the entire VM by submitting ~1 million unique 'Elixir.*' strings to any API endpoint with :module-typed attributes. Vendor patch released in commit 7031103 (v3.22.0). No public exploit identified at time of analysis, though the advisory provides detailed proof-of-concept code demonstrating trivial exploitation via repeated API requests.
Technical ContextAI
The vulnerability exploits a fundamental property of the Erlang BEAM virtual machine: atoms are immutable, globally unique identifiers stored in a fixed-size table (default ~1,048,576 entries) that are never garbage-collected. The Ash Framework's Ash.Type.Module type handler calls Module.concat([value]) on any user-supplied string beginning with 'Elixir.' before verifying module existence via Code.ensure_loaded?/1. Module.concat/1 unconditionally converts strings to atoms using String.to_atom/1 internally, permanently consuming an atom table slot even when validation fails. This violates Erlang security best practices that mandate String.to_existing_atom/1 for untrusted input, which only succeeds if the atom already exists in the table. The same vulnerable pattern appears in both cast_input/2 (line 105-113) and cast_stored/2 (line 141) functions. The root cause is CWE-400 (Uncontrolled Resource Consumption): failure to validate input before committing limited system resources.
RemediationAI
Upgrade to Ash Framework version 3.22.0 or later, released February 2025, which replaces unsafe Module.concat([value]) calls with String.to_existing_atom(value) wrapped in exception handling. The fix is available in commit 7031103da38cd1366cec8c96d6bcdc9b989aa3c2 (https://github.com/ash-project/ash/commit/7031103da38cd1366cec8c96d6bcdc9b989aa3c2) and the official v3.22.0 release (https://github.com/ash-project/ash/releases/tag/v3.22.0). For applications unable to upgrade immediately, implement input validation at the API boundary to whitelist allowed module names before they reach Ash.Type.Module, or remove public access to :module-typed attributes by setting public?: false and migrating to enumerated string types with controlled vocabularies. Review all resource definitions for attributes and arguments of type :module and assess whether user input can reach these fields. Full advisory and remediation guidance at https://github.com/advisories/GHSA-jjf9-w5vj-r6vp.
Same weakness CWE-400 – Uncontrolled Resource Consumption
View allSame technique Denial Of Service
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-jjf9-w5vj-r6vp