Skip to content

CVE-2026-9082: Verified Repro With Script Download

CVE-2026-9082: Drupal core: unauthenticated SQL injection via JSON:API filter array keys

CVE-2026-9082 is verified against drupal/core · composer. Affected versions: 8.9.0 -> <10.4.10, 10.5.0 -> <10.5.10, 10.6.0 -> <10.6.9, 11.0.0 -> <11.1.10, 11.2.0 -> <11.2.12, 11.3.0 -> <11.3.10. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00133.

REPRO-2026-00133 drupal/core · composer RCE May 22, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.8
Reproduced in
21m 27s
Tool calls
335
Spend
$3.36
01 · Overview

What Is CVE-2026-9082?

CVE-2026-9082 is a critical SQL injection vulnerability (CWE-89) in Drupal core's Entity Query SQL backend, exploitable by an unauthenticated attacker on sites backed by a PostgreSQL database. Pruva reproduced it (reproduction REPRO-2026-00133).

02 · Severity & CVSS

CVE-2026-9082 Severity & CVSS Score

CVE-2026-9082 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-89 — Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected drupal/core Versions

drupal/core · composer versions 8.9.0 -> <10.4.10, 10.5.0 -> <10.5.10, 10.6.0 -> <10.6.9, 11.0.0 -> <11.1.10, 11.2.0 -> <11.2.12, 11.3.0 -> <11.3.10 are affected.

How to Reproduce CVE-2026-9082

$ pruva-verify REPRO-2026-00133
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00133/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-9082

Reproduced by Pruva's autonomous agents — 335 tool calls over 21 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 1,115 events · 335 tool calls · 21 min
21 minDuration
335Tool calls
259Reasoning steps
1,115Events
Agent activity over 21 min
Support
22
Repro
671
Variant
417
0:0021:27

Root Cause and Exploit Chain for CVE-2026-9082

Summary

CVE-2026-9082 is a highly critical SQL injection vulnerability in Drupal core's Entity Query SQL backend that affects only PostgreSQL-backed sites. The vulnerability exists because attacker-controlled array keys from entity query condition values are directly concatenated into named SQL placeholder identifiers in the PostgreSQL-specific Condition::translateCondition() method. An unauthenticated attacker can inject arbitrary characters (such as backticks, quotes, or SQL fragments) into the raw SQL text by providing malicious keys through JSON:API collection filter query parameters or the JSON login endpoint, resulting in SQL syntax errors or — with more sophisticated payloads — information disclosure and potential privilege escalation.

Impact

  • Package/component affected: Drupal core, specifically the PostgreSQL entity query condition handler (core/modules/pgsql/src/EntityQuery/Condition.php) and upstream condition compilation in core/lib/Drupal/Core/Entity/Query/Sql/Condition.php and ConditionAggregate.php
  • Affected versions: Drupal 8.9.0 through <10.4.10, 10.5.0 through <10.5.10, 10.6.0 through <10.6.9, 11.0.0 through <11.1.10, 11.2.0 through <11.2.12, 11.3.0 through <11.3.10
  • Risk level: Highly critical (20/25 on Drupal's scale; CVSS 3.1 base 6.5)
  • Consequences: Unauthenticated SQL injection on PostgreSQL sites. Can lead to information disclosure, privilege escalation, and in some configurations remote code execution. MySQL/MariaDB and SQLite backends are not affected.

Root Cause

The root cause is a trust boundary violation in core/modules/pgsql/src/EntityQuery/Condition.php. When processing an IN condition with case-insensitive comparison on PostgreSQL, the code iterates over $condition['value'] and appends each array key directly into the SQL placeholder name:

foreach ($condition['value'] as $key => $value) {
  $where_id = $where_prefix . $key;
  $condition['where'] .= 'LOWER(:' . $where_id . '),';
  ...
}

When JSON:API filter parameters or JSON login bodies decode JSON objects into associative PHP arrays, the attacker controls both the keys and values. A malicious key like ` (backtick) becomes part of the placeholder name: LOWER(:node_field_data_title). PDO then cannot bind :node_field_data_title`` because it was never added to the bound arguments list, producing SQLSTATE[HY093]: Invalid parameter number.

More advanced payloads can inject actual SQL fragments (e.g., using ) to close the LOWER() function call and || for PostgreSQL string concatenation), enabling boolean-blind SQL injection or error-based data exfiltration.

The fix is three array_values() calls that strip attacker-controlled keys before the vulnerable loop is reached:

  • core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
  • core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
  • core/modules/pgsql/src/EntityQuery/Condition.php

Reproduction Steps

  1. Reference script: repro/reproduction_steps.sh

  2. What the script does:

    • Ensures PostgreSQL is running and creates a fresh drupal database with user drupal/drupal
    • Prepares the Drupal core repository (clones if needed, installs Composer dependencies)
    • Vulnerable phase: Checks out Drupal 11.3.9, installs a minimal site with PostgreSQL, enables the JSON:API module, creates a page content type and node, starts PHP's built-in server, and sends an anonymous GET request to /jsonapi/node/page?filter[t][condition][path]=title&filter[t][condition][value][%60]=x
    • Fixed phase: Repeats the same setup on Drupal 11.3.10 and sends the identical exploit request
  3. Expected evidence:

    • Vulnerable 11.3.9: The JSON:API endpoint returns HTTP 500 with a SQLSTATE[HY093]: Invalid parameter number error. The response body contains the malformed SQL with the injected backtick inside the placeholder name (`:node_field_data_title``).
    • Fixed 11.3.10: The same request returns HTTP 200 with a normal JSON:API response body (data: []), confirming the injection is blocked.

Evidence

Log files
  • logs/vulnerable_response.txt — HTTP response from the exploit against 11.3.9
  • logs/fixed_response.txt — HTTP response from the same exploit against 11.3.10
  • logs/repro_output.txt — Full console output from the first reproduction run
  • logs/repro_output_2.txt — Full console output from the second (idempotency) run
Key excerpts

Vulnerable 11.3.9 (logs/vulnerable_response.txt):

HTTP_CODE:500
{"errors":[{"title":"Internal Server Error","status":"500","detail":"SQLSTATE[HY093]: Invalid parameter number: parameter was not defined: SELECT ... WHERE ((LOWER(\"node_field_data\".\"title\") = (LOWER(:node_field_data_title`)))) ..."}]}

Fixed 11.3.10 (logs/fixed_response.txt):

HTTP_CODE:200
{"jsonapi":{"version":"1.1",...},"data":[],"links":{"self":{"href":"http://localhost:8080/jsonapi/node/page?..."}}}
Environment details
  • PHP: 8.4.19 (CLI + built-in server)
  • PostgreSQL: 16.13
  • OS: Ubuntu 24.04
  • Drupal test profile: Minimal install profile with JSON:API module enabled

Recommendations / Next Steps

  1. Immediate upgrade: All Drupal sites running on PostgreSQL should upgrade to the patched releases: 10.4.10, 10.5.10, 10.6.9, 11.1.10, 11.2.12, or 11.3.10 (whichever matches their branch).
  2. Verify backend: Site operators should confirm their database backend. Sites on MySQL, MariaDB, or SQLite are not affected by this specific vulnerability.
  3. WAF tuning: If immediate patching is not possible, consider blocking or sanitizing JSON:API filter query strings that contain special characters ( , ', ", ), |, --) in filter value keys, as these are the characters that reach the vulnerable placeholder name construction.
  4. Regression testing: After upgrading, verify JSON:API filter functionality still works correctly for normal use cases, since the fix reindexes condition value arrays with array_values().

Additional Notes

  • Idempotency confirmed: repro/reproduction_steps.sh was executed twice consecutively with identical results (HTTP 500 + HY093 on 11.3.9, HTTP 200 on 11.3.10).
  • Limitations: The reproduction requires a real running Drupal site over HTTP with a PostgreSQL database. A minimal PHP harness or mock objects would not satisfy the ticket requirements and were explicitly rejected.
  • Edge cases: The vulnerability only triggers on the PostgreSQL-specific case-insensitive IN condition path. MySQL/MariaDB sites are unaffected because they use a different condition translation path that does not wrap comparisons in LOWER().

CVE-2026-9082 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:17
0:00
session startedaccounts/fireworks/models/kimi-k2p6 · cve-2026-9082 · cve-2026
0:03
0:05
0:07
web search
0:17
0:19
web search
0:48
0:50
0:51
1:10
1:10
extract_facts
no facts extracted
1:11
1:11
1:11
supportrepro
1:13
1:13
1:13
1:15
1:16
1:16
1:17

Artifacts and Evidence for CVE-2026-9082

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix CVE-2026-9082

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-9082 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-9082

How does the CVE-2026-9082 SQL injection attack work?

An unauthenticated attacker supplies malicious array keys through JSON:API collection filter query parameters or the JSON login endpoint. Because these keys are concatenated directly into SQL placeholder identifiers on PostgreSQL-backed sites, they can produce SQL syntax errors or, with more sophisticated payloads, cause information disclosure and potentially privilege escalation. The issue does not trigger on MySQL/MariaDB or SQLite backends.

Which Drupal versions are affected by CVE-2026-9082?

Drupal 8.9.0 -> <10.4.10, 10.5.0 -> <10.5.10, 10.6.0 -> <10.6.9, 11.0.0 -> <11.1.10, 11.2.0 -> <11.2.12, and 11.3.0 -> <11.3.10 are affected, and only on sites using a PostgreSQL database.

How severe is CVE-2026-9082?

It is rated highly critical (20/25 on Drupal's severity scale): unauthenticated SQL injection on PostgreSQL sites that can lead to information disclosure, privilege escalation, and in some configurations remote code execution.

How can I reproduce CVE-2026-9082?

Download the verified script from this page and run it in an isolated environment against a Drupal core build in the affected version ranges above, configured with a PostgreSQL database. It sends unauthenticated JSON:API collection filter requests with malicious array keys and shows the injected SQL reaching the PostgreSQL condition compiler on the vulnerable build.
11 · References

References for CVE-2026-9082

Authoritative sources for CVE-2026-9082 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.