critical
CVE
CVE-2026-9082
CWE
CWE-89
Affected Surface
drupal/core >=8.9.0 <10.4.10 on PostgreSQL, drupal/core >=10.5.0 <10.5.10 on PostgreSQL, drupal/core >=10.6.0 <10.6.9 on PostgreSQL, drupal/core >=11.0.0 <11.1.10 on PostgreSQL, drupal/core >=11.2.0 <11.2.12 on PostgreSQL, drupal/core >=11.3.0 <11.3.10 on PostgreSQL, Drupal 8.9 and 9.5 sites requiring manual hotfix patches
Drupal published SA-CORE-2026-004 on 20 May 2026 for a highly critical SQL injection in Drupal core. On 22 May, Drupal updated the advisory to say exploit attempts were being detected in the wild. CISA added CVE-2026-9082 to the Known Exploited Vulnerabilities catalog the same day with a 27 May remediation due date for covered federal agencies.
The affected project is drupal/core when backed by PostgreSQL. Drupal sites using MySQL, MariaDB, or SQLite are not affected by this specific SQL injection path, although the same maintenance releases also carry Symfony and Twig dependency security updates that all Drupal operators should apply.
Affected versions
The vulnerable package is drupal/core from Packagist, in these ranges when the site uses PostgreSQL:
>= 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
Drupal 7 is not affected. Drupal 8 and Drupal 9 are end-of-life, but Drupal published best-effort patches for 8.9 and 9.5 because of the severity and exploitation status.
Fixed versions:
- Drupal
11.3.10 - Drupal
11.2.12 - Drupal
11.1.10 - Drupal
10.6.9 - Drupal
10.5.10 - Drupal
10.4.10
Vulnerable component
The bug is in Drupal core’s database abstraction API, specifically the PostgreSQL path used by EntityQuery condition handling. Public reporting identifies pgsql/src/EntityQuery/Condition.php as the important driver-specific location.
The core issue is structural injection into query construction. Values are normally parameterized, but attacker-controlled PHP array keys can influence SQL placeholder construction. The simplified shape is:
// Simplified: user-controlled structure enters an EntityQuery condition.
$query = \Drupal::entityQuery('node')
->condition($field, $request_value);
// PostgreSQL condition builder recursively converts array structures
// into SQL fragments and placeholders.
$condition->compile($connection, $queryPlaceholder);
If untrusted request data can affect the shape of an array passed into a condition, the dangerous input is not only the scalar value. The key or nested structure can become part of how Drupal constructs PostgreSQL SQL fragments.
A simplified vulnerable pattern looks like:
foreach ($condition as $key => $value) {
$placeholder = ':' . $key;
$where[] = "{$column} = {$placeholder}";
$arguments[$placeholder] = $value;
}
In safe code, the placeholder name and SQL fragment are derived from trusted compiler state, while only $value is attacker-controlled. In the vulnerable PostgreSQL path, crafted request structures could move attacker influence into the structural side of the query builder.
Why PostgreSQL only
Drupal’s advisory is explicit that the SQL injection only affects PostgreSQL-backed sites. That makes inventory more subtle than normal SCA:
- A
composer.lockcontainingdrupal/corein an affected range is necessary but not sufficient for this specific SQL injection. - The database backend and site configuration decide exposure.
- Multi-site Drupal deployments can have mixed backends.
- Containers and Helm charts may hide the effective backend in environment variables or secret-mounted settings.
Hunting should combine package version data with Drupal settings:
rg "drupal/core" composer.lock
rg "'driver'\\s*=>\\s*'pgsql'|\"driver\"\\s*=>\\s*\"pgsql\"" web/sites config .
rg "DATABASE_URL=.*postgres|pgsql:host|postgresql://" .
For Kubernetes and platform deployments, also search Helm values, sealed secrets, external secret templates, and environment-variable definitions for PostgreSQL DSNs.
Exploitability
The Drupal advisory states that anonymous users can exploit the issue. That means a publicly exposed vulnerable site does not require an authenticated account, a CSRF primitive, or privileged content-editor access to reach the bug.
Impact depends on schema, exposed routes, enabled modules, and database permissions, but the high-end outcomes are serious:
- Arbitrary SQL read paths and information disclosure.
- Data tampering through injected SQL.
- Privilege escalation inside Drupal by modifying user, role, or session state.
- Remote code execution in configurations where SQL-level writes can affect executable templates, cached PHP-adjacent data, module-controlled behavior, or chained application features.
Because the primitive lives under Drupal’s database abstraction layer, defenders should not only monitor a single URL. JSON:API, Views, exposed filters, contrib modules, and custom code that build EntityQueries from request parameters are all worth reviewing.
Patch and mitigation
Patch to the fixed release for the deployed branch. If the site is on Drupal 8.9 or 9.5, apply Drupal’s best-effort manual patch as an emergency stopgap and plan an upgrade off end-of-life branches.
Operational response:
- Patch
drupal/coreand redeploy from a clean build artifact. - Confirm the running container or host is actually on the patched code, not only the lockfile.
- Inventory PostgreSQL-backed sites separately from MySQL, MariaDB, and SQLite sites.
- Apply WAF or reverse-proxy rules for suspicious nested array query parameters reaching JSON:API, Views, or custom EntityQuery-backed endpoints while patching is underway.
- Review web logs from at least 20 May onward for probing against Drupal routes that accept complex filter structures.
- Review database audit logs for unusual reads, writes, role changes, account changes, and schema modifications.
- Rotate credentials if there is evidence of SQL injection success, especially database credentials reused across environments.
Useful checks after deployment:
composer show drupal/core
drush status --field=drupal-version
drush sql:query "select version();" # verify backend if safe in your environment
Do not treat non-PostgreSQL sites as done if they are still on an older core release. The SQL injection may not apply, but the coordinated Drupal release includes dependency security fixes that remain relevant.
Detection ideas
Look for request patterns that suggest structural manipulation rather than ordinary parameter values:
- Deeply nested query-string arrays on Drupal JSON:API or Views endpoints.
- Repeated bracketed keys in filters, sorts, or condition-like parameters.
- PostgreSQL syntax fragments, casts, comments, or boolean operators appearing in array keys.
- Sudden anonymous traffic spikes to routes that normally receive authenticated or low-volume filter requests.
Example log hunt patterns:
rg "%5B|\\[.*\\]|UNION|SELECT|pg_sleep|::|--|/jsonapi|views/ajax" access.log*
If exploit attempts are found, preserve web logs, Drupal watchdog logs, reverse proxy logs, database logs, and full deployment metadata. The gap between disclosure, exploit attempts, and KEV listing was short enough that routine patch windows may have overlapped with active scanning.