Sep 25, 2026·5 min read·4 visits
The Elixir Ash framework failed to check the 'public?' flag on action arguments when processing parameters with string keys or executing atomic updates, allowing unauthorized users to modify private parameters.
A parameter injection vulnerability exists in the Ash framework for Elixir, where untrusted string-keyed maps can bypass the 'public?: false' restriction on action arguments. An attacker can leverage this bypass to inject and overwrite private arguments, resulting in unauthorized data modification or privilege escalation depending on the target application's design.
The Ash framework is an Elixir resource-oriented application development library. Within Ash, developers can define action arguments and specify access constraints using the 'public?: false' attribute. This design ensures that certain parameters remain private, intended only for internal, trusted server-side operations (such as assigning authorization attributes or system metadata).
When constructing changeset objects from user inputs, the Ash framework is designed to sanitize external maps and discard parameters matching these private argument declarations. This establishes a security boundary separating untrusted HTTP parameter inputs from high-integrity internal variables.
However, a critical validation gap in the parameter-casting engine breaks this safety guarantee. By exploiting inconsistencies in the framework's internal argument lookup helpers, remote attackers can bypass these filtering boundaries. This results in an improperly controlled modification of dynamically-determined object attributes (CWE-915), also known as mass assignment or parameter pollution.
The core flaw stems from how Ash's internal parameter-processing engine resolves action arguments from incoming maps. The framework handles input maps containing either atom-keyed data (typically generated internally) or string-keyed data (typically parsed from JSON or URL-encoded web forms).
During regular changeset evaluation (such as executing 'for_create/3', 'for_update/3', or 'for_destroy/3' actions), parameter lookup is routed through the 'get_action_argument/2' helper function in 'lib/ash/changeset/changeset.ex'. While the atom-keyed lookup clause validated the 'public?' state of the target argument, the clause processing string-keyed inputs simply checked for string equivalence. It did not verify whether the matched argument was public.
In addition, the atomic and bulk update execution paths (such as 'fully_atomic_changeset/4') relied on the 'has_argument?/2' helper function. Prior to remediation, both the atom-keyed and string-keyed clauses in this function entirely omitted the 'public?' verification check. Consequently, any parameter matching a private argument would be accepted and applied during atomic execution, regardless of the key format.
The vulnerability was resolved by introducing strict public-visibility filters within all lookup paths inside 'lib/ash/changeset/changeset.ex'. The official patch ensures that 'public?' checks are consistently applied before returning any matched argument.
Below is the relevant portion of the codebase demonstrating the vulnerable structure and the corresponding structural changes implemented in the fix:
@@ -3520,15 +3520,15 @@ defmodule Ash.Changeset do
end
defp get_action_argument(action, name) when is_binary(name) do
- Enum.find(action.arguments, &(to_string(&1.name) == name))
+ Enum.find(action.arguments, &(&1.public? && to_string(&1.name) == name))
end
defp has_argument?(action, name) when is_atom(name) do
- Enum.any?(action.arguments, &(&1.name == name))
+ Enum.any?(action.arguments, &(&1.public? && &1.name == name))
end
defp has_argument?(action, name) when is_binary(name) do
- Enum.any?(action.arguments, &(to_string(&1.name) == name))
+ Enum.any?(action.arguments, &(&1.public? && to_string(&1.name) == name))
end
defp validate_attributes_accepted(changeset, %{accept: nil}), do: changesetBy adding the '&1.public?' validation to 'get_action_argument/2' and 'has_argument?/2', the lookup function returns nil when processing untrusted inputs that target private properties. The framework then safely discards or rejects the parameter as non-existent.
To exploit this vulnerability, an attacker must identify an Ash action that exposes public parameters alongside at least one private argument ('public?: false' or legacy 'private?: true') which controls security-critical behavior. Typical targets include fields that define tenant constraints, target ownership, or execution state.
Consider an application that uses a private argument to dictate the security context of a profile update. The developer sets up an update action where a private argument ':acting_user_id' is expected to be programmatically set by a router or controller plug. If policies rely on this private argument to authorize the modification, the logic becomes compromised.
An attacker sends an HTTP request with string-keyed fields containing the parameter they wish to overwrite. Because the web server parses incoming JSON payloads as string-keyed maps (e.g., %{"acting_user_id" => "victim_id"}), the vulnerable Ash engine maps the input to the private argument. The engine bypasses the public visibility check, binds the attacker's value, and runs the action with elevated or spoofed parameters.
This vulnerability has been assigned a CVSS v4.0 Base Score of 5.9 (Medium). The vector string indicates that while the local complexity and execution context limit direct remote code execution out-of-the-box, the impact on integrity is high (VI:H) when the vulnerability is successfully exploited.
In applications that use private arguments to enforce data isolation, tenant constraints, or authorization context, exploitation allows users to perform actions on behalf of other entities. This can lead to unauthorized data disclosure, unauthorized modification of sensitive configuration records, or a complete bypass of application-level access controls.
The overall impact depends heavily on the specific domain logic. If private arguments are not used to make security-critical decisions or enforce access control boundaries, the real-world impact may be limited to localized data corruption.
The primary remediation for this vulnerability is upgrading the 'ash' dependency to version 3.29.3 or later. This release permanently patches the parameter resolution functions and enforces proper visibility checks across both atomic and standard execution flows.
If upgrading is not immediately possible, applications can implement input-filtering middleware. Developers should sanitize incoming maps inside their Phoenix controllers or custom Plugs before passing the parameters to the Ash changeset functions. This is achieved by explicitly deleting keys that match private arguments from the parameters map.
# Temporary Plug or Controller Sanitization
def sanitize_params(conn, _opts) do
params = conn.params
cleaned_params = Map.drop(params, ["acting_user_id", "private_config_flag"])
%{conn | params: cleaned_params}
endSecurity teams should conduct code searches across their repositories to identify instances of 'public?: false' on action definitions to ensure proper awareness of potential exposure vectors.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ash ash-project | >= 3.0.0, < 3.29.3 | 3.29.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-915 |
| Attack Vector | Local |
| CVSS v4.0 | 5.9 (Medium) |
| EPSS Score | 0.00367 |
| Impact | High Integrity Violation |
| Exploit Status | poc |
| KEV Status | Not Listed |
The product receives input and dynamically determines which attributes of an object should be modified, but it does not properly control which attributes can be modified, allowing unauthorized attackers to modify sensitive attributes.
An improper authentication vulnerability (CWE-287) exists in the SAML backend of the social-auth-core package before version 5.0.0. The Assertion Consumer Service (ACS) endpoint does not verify whether incoming SAML assertions match a previously initiated AuthnRequest in the user's session. This permits an attacker with credentials on a shared Identity Provider to perform a 'Session Donor' attack, permanently linking their SAML identity to an authenticated victim's account and achieving full, persistent account takeover.
An identity binding collision vulnerability in the Vend OAuth2 backend of python-social-auth (social-core) before version 5.0.0 allows unauthenticated remote attackers to take over local accounts in multi-tenant configurations. The flaw stems from relying on shop-local numeric user IDs as global social-auth identifiers, leading to collisions when identical IDs exist across distinct tenants.
A Login Cross-Site Request Forgery (Login CSRF) vulnerability was discovered in the social-auth-core library prior to version 5.0.0 when utilizing the LoginRadius authentication backend. The backend explicitly disabled state token validation during the authentication callback, allowing attackers to link their identities to victim sessions.
An authentication bypass vulnerability exists in the VKontakte App backend of social-auth-core prior to version 5.0.0. The vulnerability allows remote attackers to bypass cryptographic signature verification and gain unauthorized access to arbitrary accounts by omitting the signature parameter.
CVE-2026-57179 is a critical Session Fixation and Login Cross-Site Request Forgery (CSRF) vulnerability in python-social-auth's core library (social-auth-core) prior to version 5.0.0. The vulnerability allows remote attackers to force arbitrary state transitions and bind third-party social credentials to a victim's session, leading to complete account takeover.
A Server-Side Request Forgery (SSRF) vulnerability exists in the Contao Open Source Content Management System (CMS) within the Feed Reader front-end module. When processing RSS feed configurations, the module initiates outbound HTTP connections using a default HTTP client that lacks loopback and private network controls. Authenticated backend users with permissions to configure frontend modules can exploit this flaw to coerce the server into sending requests to internal endpoints, loopback addresses, and cloud instance metadata services.