This is a master key that only fits one brand of lock, but too many front doors still use that lock
CVE-2026-9082 is an unauthenticated SQL injection in Drupal core's database abstraction layer, specifically the PostgreSQL EntityQuery path. The official affected ranges are >= 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; Drupal 8.9 and 9.5 received best-effort manual patches because they are already end-of-life. The important catch is that the SQLi path only applies to sites using PostgreSQL, and practical reachability usually depends on JSON:API, Views exposed filters, autocomplete, or custom code feeding user-controlled arrays into EntityQuery::condition().
Vendor 9.8 CRITICAL overshoots the fleet-wide reality because it scores the worst case as if every Drupal site were equally reachable and equally exposed. In practice, the vulnerable population is sharply narrowed by the database backend requirement and route/module preconditions, which is why this lands as HIGH, not CRITICAL, for patch prioritization. What keeps it high anyway is the ugly combination of unauthenticated remote reachability, public PoC material, KEV listing, and observed exploitation attempts within days of disclosure.
4 steps from start to impact.
Find a PostgreSQL-backed Drupal target
- Target Drupal site is reachable from the internet
- Target runs an affected Drupal core version
- Target specifically uses PostgreSQL
- A large share of Drupal deployments use MySQL or MariaDB, so the backend requirement immediately shrinks the victim pool
- Internal-only Drupal apps are out of reach for direct internet exploitation
- Asset discovery tools can find Drupal, but not reliably infer PostgreSQL from the outside
runZero published guidance for finding Drupal instances, but backend DB type usually requires host-side verification.Reach the vulnerable request path
dinosn/drupal-sa-core-2026-004-lab demonstrates JSON:API-based reproduction. This is still unauthenticated remote, but it is not every URL on every Drupal site.- A vulnerable endpoint is enabled and reachable
- The request path preserves attacker-controlled array keys into
EntityQuery::condition() - Anonymous access or a low-privilege path exists to the target content
- Sites without JSON:API, exposed Views filters, autocomplete exposure, or similar custom code are much harder to hit
- Anonymous access may be restricted on some hardened deployments
- Reverse proxies or WAFs may already normalize or block suspicious parameter structures
Inject through array-key placeholder manipulation
array_values() so attacker-supplied keys are discarded. Public material includes a detection PoC and a reproducible lab, which means defenders should assume exploit adaptation is well within commodity attacker skill.- Request reaches the vulnerable PostgreSQL handler unchanged
- Application uses array-valued conditions such as
INorNOT INon reachable fields - Input validation does not reject malformed key names
- This is more specialized than classic
' OR 1=1 --value injection - Some probes only produce SQL errors or blind extraction conditions rather than clean interactive exploitation
- WAF SQLi signatures can catch many metacharacter-heavy payloads if deployed in block mode
SQLSTATE[HY093] / invalid parameter count errors as a strong indicator.Turn SQLi into real impact
- Successful SQL injection is achieved
- Database account has access to sensitive tables
- For worst-case outcomes, database and app configuration permit follow-on abuse
- OS-level or full application takeover is configuration-dependent, not guaranteed by the bug alone
- Least-privileged DB roles and separated infrastructure can limit blast radius
- Blind or error-based extraction may slow down high-value post-exploitation
The supporting signals.
| In-the-wild status | Yes. Drupal raised advisory exploit status, Tenable notes exploit attempts were detected in the wild, and NVD references CISA KEV inclusion. |
|---|---|
| KEV status | Listed in CISA KEV on 2026-05-22; NVD shows due date 2026-05-27 for federal remediation guidance. |
| Proof-of-concept availability | Public PoC/lab exists. Tenable links to dinosn/drupal-sa-core-2026-004-lab on GitHub; it includes a reproduction lab and generic detection PoC. |
| EPSS | User-supplied EPSS is 0.34652. I did not verify a primary-source percentile during this assessment, so do not overfit on percentile math here. |
| CVSS and what it misses | Vendor/CNA score is 9.8 with CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, which models worst-case unauthenticated remote compromise. Real-world friction lowers priority because the vulnerable population is narrowed by PostgreSQL-only exposure and route/module requirements. |
| 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. |
| Fixed versions | Upgrade to 10.4.10, 10.5.10, 10.6.9, 11.1.10, 11.2.12, or 11.3.10 as appropriate. Drupal 9.5 and 8.9 rely on manual best-effort patches rather than normal supported releases. |
| Exposure population reality | This is not all Drupal. Pantheon explicitly states its hosted sites are not affected because Pantheon does not use PostgreSQL; that is a good reminder that the reachable population is materially smaller than the vendor score implies. |
| Reachable exploit surfaces | Akamai calls out JSON:API, Views exposed filters, and entity autocomplete as practical entry paths. The GitHub lab confirms JSON:API-based reproduction and documents the array-key preconditions. |
| Disclosure and attribution | Disclosed 2026-05-20 in SA-CORE-2026-004. Drupal credits Michael Maturi as the reporter. |
noisgate verdict.
The decisive downward pressure is the population filter: this only burns the subset of Drupal running PostgreSQL and exposing the right request paths, so the vendor's blanket 9.8 does not map to most enterprise fleets. It still stays HIGH because the reachable slice is unauthenticated, public exploit material exists, and KEV means attackers are already spending time on it.
Why this verdict
- Downgrade from vendor 9.8: the bug is unauthenticated remote, but only on the PostgreSQL backend. That backend prerequisite implies a much smaller exposed population than 'all Drupal on the internet.'
- Another downward adjustment: practical exploitation usually needs JSON:API, exposed Views filters, autocomplete, or equivalent custom code to preserve attacker-controlled array keys into
EntityQuery::condition(). That means this is not uniformly reachable across every page and route. - Kept HIGH, not MEDIUM: KEV listing on
2026-05-22, observed exploit attempts, and a public GitHub lab/PoC erase any comfort you might take from the narrower population. The exposed slice is clearly attractive and already being probed.
Why not higher?
I am not calling this CRITICAL because the real-world victim pool is constrained twice: first by Drupal-on-PostgreSQL, then by the need for a reachable endpoint pattern that preserves attacker-controlled array keys. Also, vendor language says remote code execution is possible only 'in some cases,' so worst-case impact is not the default outcome across all affected hosts.
Why not lower?
I am not dropping this to MEDIUM because the attacker position is still unauthenticated remote and the target class is an internet-facing CMS. Once KEV and public PoC material enter the picture, a narrow-but-real exposed slice deserves front-of-queue handling.
What to do — in priority order.
- Block anonymous access to vulnerable query surfaces — At the reverse proxy or WAF, immediately restrict or block anonymous requests to JSON:API collection endpoints, exposed Views filter routes, and entity autocomplete paths where business impact allows. Because this CVE is KEV-listed, deploy this immediately, within hours, not on the normal HIGH 30-day cycle.
- Turn on WAF SQLi blocking — Enable SQL injection signatures in block/deny mode and verify they inspect decoded parameter names, not just values. Akamai reports tested payload coverage for existing SQLi risk groups; for this KEV case, validate and enforce the rule set within hours.
- Hunt for probe artifacts — Search web, app, and database logs for
SQLSTATE[HY093], invalid parameter count errors, and suspiciousfilter[...]keys containing metacharacters or SQL fragments. Do this immediately, within hours, because it tells you whether you are merely exposed or already being touched. - Constrain Drupal DB privileges — Review the PostgreSQL role used by Drupal and strip unnecessary rights that could turn SQLi into destructive write access or easier follow-on abuse. This does not fix the bug, but it can reduce blast radius; complete it within hours if you cannot patch at once.
- Fence internet exposure — If the affected site is not truly public-facing, put it behind VPN, IP allowlists, or upstream auth until patching is complete. Because exploitation evidence exists, make that exposure decision within hours, not next change window.
EDR alonedoes not stop the initial SQLi; by the time it sees anything, the database may already be queried or tampered with.WAF in monitor modeis not a compensating control; if you are only logging hits, the attacker still gets to execute the request.Assuming all Drupal is affectedwastes time, but the opposite mistake is worse: simply seeing Drupal and dismissing the issue without checking for PostgreSQL and exposed routes leaves real victims uncovered.
Crowdsourced verification payload.
Run this on the target Drupal host as a user who can read the Drupal codebase and sites/*/settings.php files; root is not required if file permissions allow access. Invoke it with the Drupal docroot, for example: bash verify-cve-2026-9082.sh /var/www/html/web — it checks the installed Drupal core version and whether the site is configured for PostgreSQL, then prints VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env bash
# verify-cve-2026-9082.sh
# Check Drupal core version + PostgreSQL backend for CVE-2026-9082
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
DOCROOT="${1:-}"
if [[ -z "$DOCROOT" || ! -d "$DOCROOT" ]]; then
echo "UNKNOWN - usage: $0 <drupal-docroot>"
exit 2
fi
VERSION=""
DB_IS_PGSQL="false"
ver_ge() {
# returns 0 if $1 >= $2
[[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" == "$1" ]]
}
ver_lt() {
# returns 0 if $1 < $2
[[ "$1" != "$2" ]] && ! ver_ge "$1" "$2"
}
# Try composer.lock first
if [[ -f "$DOCROOT/composer.lock" ]]; then
VERSION=$(php -r '
$f=$argv[1];
$j=json_decode(file_get_contents($f), true);
if (!is_array($j)) exit(1);
$pkgs=array_merge($j["packages"] ?? [], $j["packages-dev"] ?? []);
foreach ($pkgs as $p) {
if (($p["name"] ?? "") === "drupal/core") {
$v=$p["version"] ?? "";
$v=preg_replace("/^v/", "", $v);
echo $v;
exit(0);
}
}
exit(1);
' "$DOCROOT/composer.lock" 2>/dev/null)
fi
# Fallback to core/lib/Drupal.php constant
if [[ -z "$VERSION" && -f "$DOCROOT/core/lib/Drupal.php" ]]; then
VERSION=$(grep -E "const VERSION = '" "$DOCROOT/core/lib/Drupal.php" 2>/dev/null | sed -E "s/.*const VERSION = '([^']+)'.*/\1/" | head -n1)
fi
# Detect PostgreSQL from settings.php files
while IFS= read -r -d '' f; do
if grep -Eqi "['\"]driver['\"]\s*=>\s*['\"]pgsql['\"]|['\"]database['\"]\s*=>\s*['\"]pgsql['\"]" "$f"; then
DB_IS_PGSQL="true"
break
fi
done < <(find "$DOCROOT/sites" -type f -name settings.php -print0 2>/dev/null)
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN - could not determine Drupal core version"
exit 2
fi
if [[ "$DB_IS_PGSQL" != "true" ]]; then
echo "PATCHED - Drupal core $VERSION detected, but no PostgreSQL configuration found for this CVE"
exit 0
fi
VULN="false"
if ver_ge "$VERSION" "8.9.0" && ver_lt "$VERSION" "10.4.10"; then VULN="true"; fi
if ver_ge "$VERSION" "10.5.0" && ver_lt "$VERSION" "10.5.10"; then VULN="true"; fi
if ver_ge "$VERSION" "10.6.0" && ver_lt "$VERSION" "10.6.9"; then VULN="true"; fi
if ver_ge "$VERSION" "11.0.0" && ver_lt "$VERSION" "11.1.10"; then VULN="true"; fi
if ver_ge "$VERSION" "11.2.0" && ver_lt "$VERSION" "11.2.12"; then VULN="true"; fi
if ver_ge "$VERSION" "11.3.0" && ver_lt "$VERSION" "11.3.10"; then VULN="true"; fi
if [[ "$VULN" == "true" ]]; then
echo "VULNERABLE - Drupal core $VERSION with PostgreSQL configuration matches CVE-2026-9082 affected ranges"
exit 1
fi
echo "PATCHED - Drupal core $VERSION with PostgreSQL configuration is outside known affected ranges"
exit 0
If you remember one thing.
9.8; first identify the PostgreSQL-backed slice of your Drupal estate, then assume any internet-exposed members of that slice are urgent. Because this CVE is KEV-listed and exploitation is observed, the normal timetable is overridden: patch / mitigate immediately, within hours. If you cannot patch in the same business day, apply route blocking/WAF deny rules and exposure fences within hours as the temporary control. For recordkeeping, the noisgate mitigation SLA override here is immediate action due to KEV, while the noisgate remediation SLA for a HIGH finding is <= 180 days—but for internet-facing PostgreSQL Drupal, that is an outer limit, not an acceptable target date.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.