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

CVE-2026-93477: Ash Framework Privilege Escalation Flaw

CVE-2026-93477 is a privilege escalation vulnerability in Ash Framework that allows attackers to modify private action arguments in bulk operations. This article covers technical details, affected versions, and mitigation.

Published:

CVE-2026-93477 Overview

CVE-2026-93477 is an improperly controlled modification of dynamically-determined object attributes vulnerability in the ash-project/ash Elixir framework. The flaw allows a caller to set private action arguments through the bulk destroy and bulk update code paths. Private arguments declared with public?: false are intended for trusted server-side use only, but the bulk paths match user-supplied parameter keys against all action arguments without checking the public? flag. The issue affects ash from version 2.17.15 before 3.33.11 and is tracked under [CWE-915].

Critical Impact

Attackers who can submit parameters to a bulk destroy or bulk update action can overwrite private arguments such as acting_user_id, leading to integrity violations or privilege escalation.

Affected Products

  • ash-project/ash versions 2.17.15 through 3.33.10
  • Applications exposing bulk actions via AshJsonApi
  • Applications exposing bulk actions via AshGraphql or controllers forwarding parameters to Ash.bulk_destroy/4 or Ash.bulk_update/4

Discovery Timeline

  • 2026-09-25 - CVE CVE-2026-93477 published to NVD
  • 2026-09-25 - Last updated in NVD database

Technical Details for CVE-2026-93477

Vulnerability Analysis

The vulnerability resides in Ash.Actions.Destroy.Bulk.base_changeset/5 and Ash.Actions.Update.Bulk.base_changeset/5. Both functions iterate over keys in a caller-supplied parameter map and match them against all declared action arguments. The matching logic does not inspect the public? attribute, so any argument name is a valid target. Matched values are then applied to the base changeset used for the bulk operation.

A related issue, CVE-2026-55736, previously fixed the non-bulk changeset path to strip private arguments from user-supplied parameters. The bulk destroy and bulk update paths were not covered by that fix. As a result, a user who can reach a bulk action can set any private argument referenced by an arg(...) template in the action's changes or validations.

The EPSS score for this issue is 0.197% (percentile 8.452).

Root Cause

The root cause is missing authorization-boundary enforcement during dynamic attribute binding. The Enum.find/2 lookup over action.arguments treats every argument as externally settable. Private arguments, which the framework otherwise guards via Ash.Changeset.set_private_argument/3, become writable through the bulk interface.

Attack Vector

An attacker submits a parameter whose key matches the name of a private argument on a bulk destroy or bulk update action. If that argument drives authorization logic, record ownership, or audit metadata, the attacker can impersonate another principal or bypass integrity controls. Exploitation requires the ability to invoke a bulk action, typically through an API layer such as AshJsonApi or AshGraphql.

text
        argument =
          if is_binary(key) do
            Enum.find(action.arguments, fn arg ->
-              to_string(arg.name) == key
+              arg.public? && to_string(arg.name) == key
            end)
          else
            Enum.find(action.arguments, fn arg ->
-              arg.name == key
+              arg.public? && arg.name == key
            end)
          end

Source: GitHub Commit 6b7ac53. The patch adds the arg.public? check in both lib/ash/actions/destroy/bulk.ex and lib/ash/actions/update/bulk.ex, blocking private arguments from being resolved via user-supplied keys.

Detection Methods for CVE-2026-93477

Indicators of Compromise

  • Bulk destroy or bulk update API requests whose parameter maps contain keys matching private argument names such as acting_user_id, tenant_id, or audit metadata fields.
  • Audit records where the acting principal recorded by an Ash action differs from the authenticated session principal.
  • Unexpected ownership changes or deletions correlated with AshJsonApi or AshGraphql bulk endpoints.

Detection Strategies

  • Review application logs for bulk action invocations and compare submitted parameter keys against the list of public?: false arguments defined on each resource.
  • Instrument Ash.bulk_destroy/4 and Ash.bulk_update/4 call sites to log parameter keys and flag any key not declared as public?: true.
  • Add integration tests that submit private argument names to bulk endpoints and assert the values are ignored on patched versions.

Monitoring Recommendations

  • Alert on API requests to bulk endpoints that include fields never present in client code or documented schemas.
  • Monitor authorization decisions keyed on arguments like acting_user_id for discrepancies with the authenticated identity.
  • Track deployment versions of ash across services and flag any instance below 3.33.11.

How to Mitigate CVE-2026-93477

Immediate Actions Required

  • Upgrade ash to version 3.33.11 or later across all services exposing bulk actions.
  • Audit all resources for actions with private arguments referenced in changes, validations, or policy templates.
  • Restrict access to bulk destroy and bulk update endpoints to trusted roles until the upgrade is applied.

Patch Information

The fix is tracked in the GitHub Security Advisory GHSA-c2p4-p7q6-hr2j and landed in commits 6b7ac53 and 8c17434. The patch enforces public? in the argument matching on both bulk paths. Private arguments remain settable server-side through the :private_arguments option. Additional details are available in the CNA advisory and OSV entry.

Workarounds

  • Strip known private argument keys from inbound parameter maps before calling Ash.bulk_destroy/4 or Ash.bulk_update/4.
  • Wrap bulk endpoints with controller-level filters that allow only an explicit list of public argument names.
  • Temporarily disable AshJsonApi and AshGraphql bulk destroy and bulk update routes for untrusted callers.
bash
# Upgrade ash in mix.exs and fetch the patched release
# {:ash, "~> 3.33.11"}
mix deps.update ash
mix deps.get

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

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.