1. Vulnerability Background
CVE-2026-9082 is a SQL Injection vulnerability in Drupal core affecting the entity query SQL condition translation path.
What is this vulnerability?
- The flaw occurs when Drupal builds SQL conditions from entity query values arrays.
- Array keys from condition values were preserved and later used in SQL generation.
- This allowed specially crafted array keys to escape the parameterization layer and introduce SQL fragments.
Why is it critical/important?
- Any SQL Injection in Drupal core is high risk because it can be triggered through widely used query APIs.
- A successful exploit can lead to unauthorized data access, data modification, privilege escalation, and potentially full site compromise.
- Because entity queries are used in both core and contrib code, the attack surface is broad.
Affected systems/versions:
- Drupal core 8.9.0 through 10.4.9
- Drupal core 10.5.0 through 10.5.9
- Drupal core 10.6.0 through 10.6.8
- Drupal core 11.0.0 through 11.1.9
- Drupal core 11.2.0 through 11.2.11
- Drupal core 11.3.0 through 11.3.9
Patched versions:
- 10.4.10
- 10.5.10
- 10.6.9
- 11.1.10
- 11.2.12
- 11.3.10
2. Technical Details
Root cause analysis:
- The flaw is in how Drupal normalized array-valued condition data before translating it into SQL.
- In
core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.phpandcore/lib/Drupal/Core/Entity/Query/Sql/Condition.php,condition['value']arrays were passed through without resetting their keys. - In
core/modules/pgsql/src/EntityQuery/Condition.php, PostgreSQL-specific SQL generation used the original array keys directly when building named placeholders.
Attack vector and exploitation conditions:
- An attacker must be able to influence a condition value array passed into Drupal’s entity query API.
- The specific vector is array keys, not array values. A malicious key containing SQL syntax can be used to break out of a parameterized context.
- This is likely to be exposed through custom modules or contrib code that accepts user input and forwards it into
condition(..., $array, 'IN')or similar entity query conditions without normalizing the array.
Security implications:
- The vulnerability allows untrusted data to affect SQL command structure.
- It bypasses the intended parameter binding mechanism by introducing attacker-controlled tokens into query text.
- The attacker-controlled payload can be used to execute arbitrary SQL statements or alter query behavior.
3. Patch Analysis
What code changes were made?
- In
core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.phpandcore/lib/Drupal/Core/Entity/Query/Sql/Condition.php, developers added normalization of array values: if (is_array($condition['value'])) { $condition['value'] = array_values($condition['value']); } - In
core/modules/pgsql/src/EntityQuery/Condition.php, the loop was changed from: foreach ($condition['value'] as $key => $value) { to: foreach (array_values($condition['value']) as $key => $value) {
How do these changes fix the vulnerability?
array_values()strips original array keys and replaces them with sequential integer keys.- This prevents attacker-controlled string keys from being used in SQL placeholder generation.
- The translation layer now depends only on the actual values, not on array key metadata.
Security improvements introduced:
- Removal of a class of SQL injection via array-key manipulation.
- More predictable and safe entity query behavior for array-valued conditions.
- Consistency across generic SQL condition translation and PostgreSQL-specific code.
4. Proof of Concept (PoC) Guide
Prerequisites for exploitation:
- A Drupal instance running a vulnerable core version.
- Ability to execute custom PHP code or a custom module with entity query access.
- Preferably a PostgreSQL backend for the backend-specific path, although the generic fix shows the problem is broader.
Step-by-step exploitation approach:
- Create an entity query that uses an array-valued condition, for example: $query = \Drupal::entityQuery('node');
- Supply a condition value array with a malicious string key: $values = ["malicious'); DROP TABLE users; --" => 'foo'];
- Add the condition: $query->condition('status', $values, 'IN');
- Execute or inspect the query.
Expected behavior on patched code:
- The array is normalized to
['foo']. - SQL placeholders are generated from sequential numeric keys, e.g.
:where0. - No attacker-controlled SQL fragments are introduced.
Exploited behavior on vulnerable code:
- The original array key is preserved.
- SQL generation incorporates the malformed key into the query text.
- A payload like
malicious'); DROP TABLE users; --can escape the safe parameter placeholder context and execute as SQL.
How to verify the vulnerability exists:
- Inspect the compiled SQL or enable query logging.
- Look for query fragments derived from array keys rather than only from parameter values.
- Confirm that the generated SQL contains unexpected text matching your malicious key payload.
- On patched code, verify that the array key is discarded and does not appear in SQL.
5. Recommendations
Mitigation strategies:
- Upgrade Drupal core to one of the fixed versions immediately.
- If immediate upgrade is not possible, audit code that constructs entity query conditions with array values and apply
array_values()before passing arrays into conditions. - Avoid using associative array keys derived from untrusted input in SQL query builders.
Detection methods:
- Audit code paths that call
condition(..., $array, ...)or other entity query APIs with array values. - Monitor database query logs for malformed or suspicious placeholder names.
- Use static analysis to find unsanitized arrays passed to query constructors.
Best practices to prevent similar issues:
- Treat array keys as metadata, not as data safe for SQL generation.
- Normalize arrays before feeding them into SQL translation code.
- Prefer strict value arrays for query conditions and avoid associative arrays when keys are not semantically meaningful.
- Keep Drupal core updated and apply security patches promptly.