Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
💧 Hex
Not in CISA KEV

CVE-2026-34593 — ash

Fix: ash-project/ash@7031103

CVE-2026-34593 is a Uncontrolled Resource Consumption vulnerability in ash. A fix is available for ash — see the affected versions and patch details below.

Ash Framework: Ash.Type.Module.cast_input/2 atom exhaustion via unchecked Module.concat allows BEAM VM crash

Also known asGHSA-jjf9-w5vj-r6vp
Published
Apr 2, 2026
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 24, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-34593.

EPSS Exploitation Probability

via FIRST.org ↗
0.5%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs41th percentile — riskier than 41% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

Real-World Exposure

1 pkg affected
💧ash

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Hex packages — download data is not available via public APIs for these ecosystems.

Description

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
end

Vulnerable 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
end

Exploit: 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_limit

Contrast: 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
  ...
end

Additional 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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
💧Hexashall versions3.22.0mix deps.update ash

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for ash, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update ash to 3.22.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-34593 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-34593 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2026-34593. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## 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 a
O3 Security · Impact-Aware SCA

Is CVE-2026-34593 in your dependencies?

O3 Security finds CVE-2026-34593 across Hex dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2026-34593: ash DoS | O3 Security