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

CVE-2026-81636: ash_graphql DOS Vulnerability

CVE-2026-81636 is a denial of service vulnerability in ash_graphql that lets unauthenticated attackers bypass query-complexity limits and force unbounded database reads. This article covers technical details, affected versions, and mitigation.

Published:

CVE-2026-81636 Overview

CVE-2026-81636 is an Allocation of Resources Without Limits or Throttling vulnerability [CWE-770] in the ash_graphql library for the Elixir Ash framework. An unauthenticated network client can bypass the configured GraphQL query-complexity cap by using Relay connection or keyset pagination arguments (first and last) instead of offset pagination (limit). Nested queries then materialize very large fan-outs while scoring as trivially cheap under the Absinthe max_complexity check. The vulnerability affects ash_graphql versions from 0.16.23 up to but not including 1.11.0.

Critical Impact

An unauthenticated attacker can force unbounded database reads against any exposed ash_graphql endpoint, exhausting database and application resources and producing a denial-of-service condition.

Affected Products

  • ash-project ash_graphql>= 0.16.23
  • ash-project ash_graphql< 1.11.0
  • Elixir applications exposing GraphQL APIs built on the Ash framework with Relay or keyset pagination enabled

Discovery Timeline

  • 2026-08-30 - CVE-2026-81636 published to NVD
  • 2026-09-01 - Last updated in NVD database

Technical Details for CVE-2026-81636

Vulnerability Analysis

The defect lives in AshGraphql.Graphql.Resolver.query_complexity/3, the callback Absinthe invokes to score GraphQL queries before execution. The function multiplies child_complexity by the requested page size only when the argument map matches the :limit clause, which corresponds to offset pagination. Relay connections and keyset pagination pass first or last arguments instead, neither of which matches the :limit pattern. Those requests fall through to the catch-all clause that returns child_complexity + 1, so nested pagination fan-out is under-counted.

A nested query such as posts(first: 500) { edges { node { comments(first: 500) { ... } } } } scores near the minimum while the resolver reads 500 × 500 rows from the database. The same query written with limit would be rejected by the configured Absinthe max_complexity cap. The ash_graphql maintainers addressed this in version 1.11.0 by adding dedicated first and last clauses that clamp the multiplier to the action's page size.

Root Cause

The root cause is incomplete pattern coverage in the complexity function. Only offset-based pagination was accounted for, so cost estimation did not model the amplification introduced by Relay and keyset pagination.

Attack Vector

Exploitation requires no authentication and only network access to a GraphQL endpoint exposed by an affected ash_graphql version. An attacker crafts a deeply nested Relay query using first or last values approaching the action page size at each level. The request bypasses max_complexity, and the backend performs the full fan-out read.

elixir
     end
   end
 
+  # Relay connections and keyset pagination use `first`/`last` rather than `limit`;
+  # multiply by the page size here too, or the fan-out is under-counted and the
+  # complexity limit is bypassed.
+  def query_complexity(
+        %{first: first},
+        child_complexity,
+        _
+      )
+      when is_integer(first) do
+    if child_complexity == 0 do
+      1
+    else
+      first * child_complexity
+    end
+  end
+
+  def query_complexity(
+        %{last: last},
+        child_complexity,
+        _
+      )
+      when is_integer(last) do
+    if child_complexity == 0 do
+      1
+    else
+      last * child_complexity
+    end

Source: GitHub commit c3229f6 — the patch adds first and last clauses so the resolver multiplies child_complexity by the requested page size, restoring accurate cost accounting.

Detection Methods for CVE-2026-81636

Indicators of Compromise

  • Inbound GraphQL POST requests containing deeply nested selection sets that use first: or last: arguments with large integer values
  • Sudden spikes in database query duration, row-read counts, or connection-pool saturation correlated with GraphQL traffic
  • Application logs showing long-running ash_graphql resolver invocations against paginated relationships

Detection Strategies

  • Parse GraphQL request bodies at the ingress layer and flag queries whose nesting depth combined with the product of first/last values exceeds a defined budget
  • Instrument Absinthe with a custom complexity logger to record accepted queries and their computed complexity, then alert on queries that read many rows while reporting low complexity
  • Correlate authentication state with GraphQL traffic volume to identify unauthenticated clients issuing paginated queries

Monitoring Recommendations

  • Track database rows-read and query latency metrics per GraphQL operation name
  • Monitor Elixir BEAM memory and process counts for anomalous growth tied to GraphQL request handling
  • Alert on repeated identical or templated GraphQL queries from a single source IP that trigger heavy backend reads

How to Mitigate CVE-2026-81636

Immediate Actions Required

  • Upgrade ash_graphql to version 1.11.0 or later across all Elixir applications that expose GraphQL endpoints
  • Audit Ash resource actions to confirm each paginated action defines a bounded default_page_size and max_page_size
  • Rate-limit unauthenticated GraphQL traffic at the load balancer or reverse proxy until the upgrade is deployed

Patch Information

The fix is available in ash_graphql1.11.0. See the GitHub Security Advisory GHSA-mwc4-r9fc-h6mg, the CNA advisory from EEF, the OSV entry EEF-CVE-2026-81636, and the upstream commit c3229f6.

Workarounds

  • Lower the Absinthe max_complexity value to a threshold that rejects even under-counted nested queries
  • Enforce a strict maximum GraphQL query depth using an Absinthe middleware or pre-execution rule
  • Require authentication for any GraphQL operation that exposes paginated relationships
  • Cap first and last arguments at the schema level by validating input ranges before resolver execution
bash
# Update the ash_graphql dependency in mix.exs, then fetch and compile
# mix.exs
# {:ash_graphql, "~> 1.11"}
mix deps.update ash_graphql
mix deps.get
mix compile --force

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.