Aug 26, 2026·6 min read·24 visits
An insecure Map.merge sequence in the elixir-grpc transcoding layer allows attackers to bypass tenancy controls by overriding authoritative path parameters via HTTP parameter pollution.
An authorization bypass vulnerability exists in the elixir-grpc/grpc library version 0.8.0 up to 1.0.0. Due to insecure map merging precedence inside the HTTP-to-gRPC transcoding engine, query-string parameters and request bodies can override routing path variables, allowing attackers to execute unauthorized actions on other accounts.
The Elixir gRPC library (elixir-grpc/grpc) incorporates an HTTP-to-gRPC transcoding layer within the GRPC.Server.Transcode module. This feature permits gRPC services to accept HTTP REST requests with JSON payloads and map them automatically to internal Protobuf format messages. This architectural pattern facilitates compatibility with external client applications that do not natively support standard HTTP/2-based gRPC protocols.
The vulnerability, designated as CVE-2026-48599, resides inside the parameter mapping logic of this transcoding layer. Specifically, when transcoding is enabled via the http_transcode: true option, the server processes router-extracted path variables alongside incoming query-string parameters and JSON request bodies. Due to a design flaw in the variable collection sequence, the library permits untrusted client-side inputs to overwrite authoritative route bindings.
This flaw exposes applications to HTTP Parameter Pollution (HPP) and Authorization Bypass Through User-Controlled Key (CWE-639). If an application validates user permissions against the request path but relies on the resulting gRPC struct fields for execution, an attacker can manipulate these fields to bypass authorization checks. This enables horizontal or vertical privilege escalation and unauthorized access to isolated tenant resources.
The core issue involves the evaluation order of parameter maps during the request translation process. In the Elixir programming language, the Map.merge/2 function combines two maps. If a key is present in both maps, the value from the second argument (the right-hand operand) takes precedence and overwrites the value from the first argument (the left-hand operand).
In vulnerable versions of elixir-grpc/grpc (from 0.8.0 up to, but excluding, 1.0.0), the GRPC.Server.Transcode module utilizes Map.merge/2 to combine path parameters, query-string parameters, and request body variables. The library implements three distinct clauses of the map_request/5 function to handle different configurations of the google.api.HttpRule option. In all three clauses, the merge logic incorrectly assigns the highest precedence to user-supplied inputs instead of authoritative path bindings.
When processing HTTP GET requests with an empty body, the library executes Map.merge(path_bindings, query). For POST, PUT, or PATCH requests mapping the entire request body to a catch-all asterisks parameter, it calls Map.merge(path_bindings, body_request). For mixed requests with named body fields, it reduces query-string and body maps into the path bindings map using Enum.reduce/3 with the function &Map.merge(&2, &1). In every scenario, path bindings serve as the base, allowing subsequent user-defined query parameters or body payloads to overwrite identically named keys.
To understand the mechanics of the vulnerability, we can examine the implementation within lib/grpc/server/transcode.ex. The vulnerable implementation of map_request/5 parses parameters in the following manner:
# Vulnerable implementation in lib/grpc/server/transcode.ex
def map_request(%{body: ""}, _body_request, path_bindings, query_string, req_mod) do
path_bindings = map_path_bindings(path_bindings)
query = Query.decode(query_string)
# Path bindings are passed as first parameter (overridden by query)
request = Map.merge(path_bindings, query)
Protobuf.JSON.from_decoded(request, req_mod)
endThe patch introduced in commit 33b6a095dbc91c6dee3c7b90893d7d74952e82e4 corrects the priority order by ensuring that the authoritative router-derived parameters are merged last, thereby overwriting any conflicting user-provided inputs. The remediation introduces the following changes across all three map_request/5 clauses:
# Patched implementation in lib/grpc/server/transcode.ex
def map_request(%{body: ""}, _body_request, path_bindings, query_string, req_mod) do
path_bindings = map_path_bindings(path_bindings)
query = Query.decode(query_string)
# Query parameters are passed first, ensuring path_bindings override them
request = Map.merge(query, path_bindings)
Protobuf.JSON.from_decoded(request, req_mod)
endSimilarly, for request handlers utilizing named body fields, the reduction sequence was altered from Enum.reduce([query, body_request], path_bindings, &Map.merge(&2, &1)) to Enum.reduce([query, body_request], path_bindings, &Map.merge(&1, &2)). This adjustment guarantees that the path bindings accumulator remains the overriding value at each step of the reduction.
An attack requires an environment where the transcoded endpoints extract authorization keys from path bindings. Consider a backend service providing user profile modifications at /v1/users/{user_id}/settings. A routing gateway or validation middleware verifies that the authenticated user's token matches the {user_id} segment in the HTTP request path. If this path binding is validated, the gateway forwards the request to the gRPC transcoding layer.
An attacker targeting this service attempts to modify the profile of a victim (victim_user_id). The attacker constructs an HTTP request where the path segment indicates their own validated identity (attacker_user_id), but they append a conflicting user_id inside the request body or query string.
POST /v1/users/attacker_user_id/settings HTTP/1.1
Host: vulnerable-target.local
Authorization: Bearer <attacker_token>
Content-Type: application/json
{
"user_id": "victim_user_id",
"email": "malicious@attacker-domain.com"
}Upon receiving this payload, the vulnerable server extracts path_bindings = %{"user_id" => "attacker_user_id"} and validates the token. The transcoding engine then parses the JSON body to retrieve body_request = %{"user_id" => "victim_user_id"}. Due to the insecure merge order, the resulting map contains "user_id" => "victim_user_id". The deserialized Protobuf message payload is dispatched to the backend gRPC handler, causing it to update the victim's profile and leading to account takeover.
The impact of CVE-2026-48599 is substantial, as it undermines the reliability of path-based authorization across transcoded endpoints. If an organization enforces tenant separation or access controls at the routing layer while assuming the downstream gRPC handler will process only safe parameters, the system becomes fully vulnerable to unauthorized cross-tenant operations.
The CVSS v4.0 base score is calculated as 7.6 (High) with vector string CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N. The rating is influenced by the low attack complexity and lack of user interaction requirements. Although the attack requires HTTP transcoding to be enabled, the potential consequences include high confidentiality and integrity compromise since attackers can read, overwrite, or delete sensitive data belonging to any other user inside the database.
Currently, this CVE is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and there are no reports of active exploitation in the wild. The EPSS score is recorded at 0.00273 (percentile ranking of 19.15%). However, because public proof-of-concept information is available, applications implementing vulnerable versions of elixir-grpc/grpc must be remediated immediately.
The primary remediation path is upgrading the elixir-grpc/grpc library to version 1.0.0 or higher, which includes the parameter merge priority correction. This update can be applied by adjusting the dependency list in mix.exs and executing mix deps.update grpc within the project environment.
In scenarios where immediate upgrades are blocked, teams must implement defensive validation within downstream gRPC handler functions. Developers should extract authentication credentials directly from the gRPC stream metadata or transport headers (such as x-authenticated-user-id) and cross-verify these values against the deserialized Protobuf struct fields before performing database operations.
def update_settings(request, stream) do
auth_user_id = GRPC.Stream.get_headers(stream)["x-authenticated-user-id"]
if request.user_id != auth_user_id do
raise GRPC.RPCError, status: :permission_denied, message: "Access Denied"
else
# Process the request securely
end
endAdditionally, organizations can configure API Gateways or web application firewalls (WAF) to block requests that supply query-string arguments matching restricted path variable names. This prevents malicious query pollution from reaching the Elixir runtime.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
elixir-grpc/grpc elixir-grpc | >= 0.8.0, < 1.0.0 | 1.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 7.6 (High) |
| EPSS Score / Percentile | 0.00273 / 19.15% |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
| Ransomware Use | No |
The system uses an attacker-controlled key to access a resource without verifying authorization for that resource.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.