← Back to Feed CACHED · 2026-10-21 18:05:44 · CACHE_KEY CVE-2026-108097
CVE-2026-108097 · CWE-94 · Disclosed 2026-10-14

Drupal Core Field UI unauthenticated RCE via unsafe #region_callback PHP callable execution

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like a loaded gun in a locked safe — the trigger is in core, but someone else has to open the door

CVE-2026-108097 is a remote code execution flaw in Drupal core's Field UI module (field_ui), disclosed on 14 October 2026 as SA-CORE-2026-014. The root cause is a missing validation on the #region_callback element in Field UI's table render array: any PHP callable placed in that slot is executed directly with no is_callable() check, no allow-list, and no signature restriction. The vulnerability spans an enormous range — Drupal 8.9.0 through 10.6.18, 11.0.0 through 11.3.18, 11.4.0 through 11.4.9, and 12.0.0-alpha1 through beta1. Fixed releases are 10.6.19, 11.3.19, 11.4.10, and 12.0.0-beta2; Drupal 7 is patched in 7.107.

Tenable stamps this at CVSS 9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), and Drupal rates it 19/25 Critical on their own scale. But both scores overstate real-world exploitability for most sites. The crucial detail buried in the advisory is: core alone is not directly exploitable. The vulnerable callback sits behind authenticated admin routes (Manage Display forms). Reaching it without auth requires a contributed or custom module that passes request data into that render array — and FORTBRIDGE, who published the definitive PoC, explicitly could not find a genuine public contributed module that creates this path. They had to write a synthetic bridge module for their lab chain. That friction is meaningful. The severity is real — when the chain connects, you get unauthenticated RCE — but the precondition narrows the exploitable population far below what a 9.8 implies.

"Drupal Field UI RCE is real but gated by contributed module; patch urgently, don't panic."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify Drupal target and version

The attacker fingerprints the target as Drupal via generator meta tags, X-Drupal-Cache headers, or session cookie names. Version is extracted from /core/CHANGELOG.txt or generator tags. The ExDev994 weaponized tool includes FOFA dorks for bulk reconnaissance. Over 151,000 sites were running vulnerable versions as of disclosure.
Conditions required:
  • Target runs Drupal 8.9.0–10.6.18, 11.0–11.3.18, 11.4.0–11.4.9, or 12.0.0 pre-beta2
Where this breaks in practice:
  • ~50% of Drupal assets hide version indicators per CyCognito data
  • Version alone does not confirm exploitability — contributed module is still required
Detection/coverage: Nessus plugin 214907, Qualys QID, Nuclei template available. Version fingerprinting is straightforward.
STEP 02

Probe for contributed module entry point

The attacker needs a contributed or custom module that (a) exposes a route accepting unauthenticated requests and (b) feeds request parameters into Field UI's table render array, specifically into a #region_callback slot. FORTBRIDGE's research confirmed that core's own Field UI routes are admin-only and reject unauthenticated requests. The attacker scans for known module routes (e.g., Entity List views, custom field-display endpoints) or fuzzes for render array injection points.
Conditions required:
  • Target must have a contributed/custom module that bridges unauthenticated input to the vulnerable render path
Where this breaks in practice:
  • FORTBRIDGE found no public contributed module that creates this path — they used a synthetic bridge module
  • The Entity List module (most similar match) was patched separately as CVE-2026-108099
  • Custom code creating this pattern exists but prevalence is unknown and likely low
Detection/coverage: WAF rules monitoring for unusual Field UI route access patterns; Drupal Steward provides partial protection for known vectors.
STEP 03

Inject malicious PHP callable via #region_callback

Through the contributed module's unauthenticated route, the attacker injects a crafted value into the #region_callback render array element. Because Field UI performs zero validation on this value — no is_callable() check, no allow-list — any PHP callable (including system, passthru, exec, or arbitrary class methods) is accepted and queued for execution during table rendering.
Conditions required:
  • Unauthenticated route from Step 2 passes attacker input to the render array
  • PHP callable injection is unfiltered
Where this breaks in practice:
  • Some WAFs may block obvious PHP function names in POST/GET parameters
  • Drupal Steward's WAF rules cover *known* vectors but not all possible module entry points
Detection/coverage: Application-layer logging of render array construction; PHP disable_functions may block common callables but determined attacker uses alternatives.
STEP 04

Achieve file disclosure

The FORTBRIDGE chain demonstrates an intermediate step: using the callable injection to read arbitrary files from the server filesystem. This confirms the vulnerability is live and extracts configuration data (e.g., settings.php containing database credentials, hash salts, and potentially API keys). This is the file-disclosure stage of the ExDev994 multi-stage pipeline.
Conditions required:
  • Successful callable injection from Step 3
  • PHP process has read access to target files
Where this breaks in practice:
  • SELinux/AppArmor policies may restrict PHP process file reads
  • Containerized deployments may limit accessible filesystem
Detection/coverage: File integrity monitoring (FIM) won't catch reads; look for anomalous PHP file_get_contents or include calls in application logs.
STEP 05

Escalate to remote code execution

The attacker pivots from file disclosure to arbitrary command execution. The ExDev994 tool progresses through callable leaking to identify available PHP functions, then chains to system() or equivalent for OS command execution. The result is a web shell or direct command output — full RCE as the web server user (typically www-data or apache). FORTBRIDGE confirmed the full chain: file disclosure to command execution, no authentication required.
Conditions required:
  • All prior steps succeed
  • PHP disable_functions does not block all execution primitives
Where this breaks in practice:
  • Hardened PHP configurations with restrictive disable_functions lists may block direct command execution
  • Container environments may limit lateral movement from the web server user
Detection/coverage: EDR/HIDS monitoring for process spawning from PHP-FPM/Apache worker; web shell detection signatures; outbound connection monitoring from web tier.
03 · Compensating Control

1
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Uninstall the field_ui module immediately — Drupal's official interim workaround. The field_ui module provides admin UI for managing fields and display modes — it is not needed at runtime on production sites. Removing it eliminates the vulnerable callback entirely. Run drush pm:uninstall field_ui or remove from config. Deploy within 30 days per the noisgate mitigation SLA for HIGH, but given the weaponized PoC, doing this within 72 hours is strongly recommended.
2
HIGH 7.5→MEDIUM 5.5
SEVERITY REDUCED
Deploy WAF rules blocking Field UI route patterns — If you cannot uninstall field_ui (e.g., content teams actively use it in production), deploy WAF rules that block unauthenticated access to routes containing /admin/structure/, /field-ui/, and any contributed module routes that expose table rendering. Drupal Steward provides partial coverage for known vectors. This reduces but does not eliminate risk — unknown contributed module entry points may bypass route-based rules.
3
HIGH 7.5→MEDIUM 5.0
SEVERITY REDUCED
Harden PHP disable_functions — Add system, exec, passthru, shell_exec, proc_open, popen, pcntl_exec to PHP's disable_functions in php.ini. This breaks the RCE stage of the chain even if the callable injection succeeds — the attacker can still achieve file disclosure but cannot execute OS commands. Apply across all Drupal PHP-FPM pools.
4
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Patch to fixed Drupal version — Apply the vendor patch: upgrade to 10.6.19, 11.3.19, 11.4.10, or 12.0.0-beta2 (or 7.107 for D7 LTS). This is the definitive remediation. Per noisgate remediation SLA for HIGH, complete within 180 days. Given PoC availability, targeting 30 days is prudent.
What doesn't work
  • Restricting admin access via IP allowlist alone — The unauthenticated exploitation path goes through contributed module routes, not admin routes. IP-restricting /admin/* does not block the contributed module entry point.
  • Updating contributed modules only — The vulnerability is in Drupal *core's* Field UI, not in any specific contributed module. Patching Entity List (CVE-2026-108099) closes one path but the core flaw remains exploitable via other bridges.
  • PHP open_basedir restrictions — While this limits file disclosure scope, it does not prevent the callable injection or command execution stages. The attacker can still achieve RCE within the allowed directory tree.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNo confirmed exploitation as of 22 Oct 2026. Not on CISA KEV. Drupal marked exploit maturity as E:Theoretical at disclosure but warned exploits could emerge 'within hours or days.'
Proof-of-conceptMultiple PoCs public. FORTBRIDGE (Adrian Tiron) published full technical analysis + lab exploit code. ExDev994/CVE-2026-108097 is a weaponized Python3 multi-stage tool (detect → write → sink → callable leak → file disclosure → RCE) with FOFA dorks. FurkanKAYAPINAR/CVE-2026-108097 is a defensive detector. Sploitus indexes ≥2 exploit entries. Critical caveat: all PoCs require a bridge module — no public contributed module enables the unauth chain.
EPSS scoreNot yet scored by FIRST (empty result from EPSS API as of 22 Oct 2026). CVE is only 8 days old; EPSS typically populates within 30 days.
KEV statusNot listed. No CISA Known Exploited Vulnerabilities entry as of 22 Oct 2026.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical) per Tenable/NVD. Drupal's own scale: 19/25 Critical (AC:Basic/A:None/CI:All/II:All/E:Theoretical/TD:Default). Both assume the unauth path is reachable — which requires a contributed module.
Affected versionsDrupal core 8.9.0–10.6.18, 11.0.0–11.3.18, 11.4.0–11.4.9, 12.0.0-alpha1 through beta1. Drupal 7 < 7.107 also affected.
Fixed versions10.6.19, 11.3.19, 11.4.10, 12.0.0-beta2, 7.107 (LTS). Released 14 October 2026. Drupal 8.x and 9.x are EOL — no patches available.
Exposure surface151,000+ Drupal sites (28.5% of reporting sites) running vulnerable versions per CyCognito. Sectors: Consumer Staples (30.5%), Industrials (27.4%). ~50% of assets hide version. Geo concentration: US, Netherlands, Germany.
Disclosure timelineDisclosed 14 Oct 2026 (SA-CORE-2026-014). FORTBRIDGE technical writeup + PoC followed within days. ExDev994 weaponized tool published 20 Oct 2026.
Researcher / reporterDrupal Security Team (advisory). Adrian Tiron / FORTBRIDGE (PoC + technical analysis). ExDev994 (weaponized exploit tool).

Sources.

  1. Drupal SA-CORE-2026-014 (Official Advisory)
  2. BleepingComputer — Drupal patches critical RCE flaws
  3. SecurityOnline — Technical Details and PoC Published
  4. CyCognito — Emerging Threat Analysis
  5. SOCRadar — Drupal Patches Critical RCEs
  6. Tenable — CVE-2026-108097
  7. FurkanKAYAPINAR/CVE-2026-108097 (Defensive Detector)
  8. Sploitus — ExDev994 Weaponized PoC
05 · The Call

Final Verdict
↓ DOWNGRADED to HIGH (7.5/10)

Why this verdict

  • Contributed module gate is real friction. The CVSS 9.8 assumes direct unauthenticated exploitation, but Drupal's own advisory and FORTBRIDGE's research both confirm core alone is not directly exploitable. The vulnerable callback sits behind admin-only routes. An unauthenticated path requires a contributed or custom module that bridges user input to the render array — and FORTBRIDGE explicitly found no genuine public contributed module that does this. This materially narrows the exploitable population.
  • PoC is weaponized but bridge-dependent. ExDev994's Python tool is a full pipeline (detect → RCE), but it still needs a target-side module that creates the unauthenticated entry point. Without that bridge, the tool's scan stages will find the Drupal version but fail at the sink-probing stage. The PoC proves the *mechanism* but not mass exploitability.
  • No active exploitation or KEV listing. Eight days post-disclosure with weaponized PoC available, there are zero confirmed in-the-wild incidents. Drupal marked exploit maturity as Theoretical. CISA has not added it to KEV. This suggests the contributed module requirement is a genuine barrier, not a theoretical one.
  • Role multiplier: Drupal is a web application CMS, not canonical infrastructure (not a hypervisor, domain controller, IdP, PAM, or network edge appliance). (a) *Low-value role:* dev/staging sites, personal blogs — compromise is nuisance-level. (b) *Typical role:* corporate marketing sites, content portals — compromise yields web server access + database credentials (PII, session tokens), blast radius is application-tier. (c) *High-value role:* government portals, e-commerce sites handling PII/payment data, intranet portals with SSO integration — compromise could yield regulated data and pivot opportunities. However, even in the high-value role, the blast radius is host→application, not domain→fleet. Drupal compromise does not inherently give domain admin, fleet control, or supply-chain pivot. The high-value outcome (data exfiltration from a government portal) warrants HIGH but not CRITICAL given the contributed module gate.
  • Large but uncertain exposure surface. 151K+ vulnerable sites is significant, but the *exploitable* subset (those with the right contributed/custom module) is unknown and likely a small fraction. The uncertainty prevents going lower than HIGH — some non-trivial number of sites probably have custom code that inadvertently creates the bridge.

Why not higher?

The contributed module requirement is not a theoretical defense — it is a validated friction point confirmed by the researcher who wrote the PoC. FORTBRIDGE explicitly could not find a public module that enables the unauth chain. Core-only Drupal installations are not exploitable. Drupal is not a canonical high-value infrastructure component (not a DC, hypervisor, IdP, or network edge), so the deployment-role floor does not mandate CRITICAL. No active exploitation exists despite 8 days of public PoC availability.

Why not lower?

The vulnerability *mechanism* is confirmed: when the bridge exists, the chain delivers unauthenticated RCE with no user interaction — the most severe outcome class. Weaponized tooling is public and includes bulk-scanning capability (FOFA dorks). 151K+ sites are running vulnerable versions. The contributed module requirement is friction, not an impenetrable barrier — custom code on Drupal sites is common, and the specific pattern (feeding request data into render arrays) is a plausible development mistake. Drupal's own security team rated this Critical and issued advance warning. Dropping below HIGH would undercount the risk to the fraction of sites that do have the bridge.

06 · Verification

Crowdsourced verification payload.

Run this script on each Drupal web server host as the user that owns the Drupal codebase (or root). It checks the Drupal core version against patched releases and whether field_ui is enabled. Example: chmod +x check_cve_2026_108097.sh && ./check_cve_2026_108097.sh /var/www/html where the argument is your Drupal docroot. No special privileges needed beyond read access to the Drupal filesystem.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_108097.sh — Detect CVE-2026-108097 (Drupal Field UI RCE)
# Usage: ./check_cve_2026_108097.sh /path/to/drupal/docroot
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN

set -euo pipefail

DOCROOT="${1:-}"
if [ -z "$DOCROOT" ] || [ ! -d "$DOCROOT" ]; then
  echo "UNKNOWN — Usage: $0 /path/to/drupal/docroot"
  exit 2
fi

# Detect Drupal major version and core version
VERSION=""
if [ -f "$DOCROOT/core/lib/Drupal.php" ]; then
  # Drupal 8/9/10/11/12
  VERSION=$(grep -oP "const VERSION = '\K[^']+" "$DOCROOT/core/lib/Drupal.php" 2>/dev/null || true)
elif [ -f "$DOCROOT/includes/bootstrap.inc" ]; then
  # Drupal 7
  VERSION=$(grep -oP "define\('VERSION', '\K[^']+" "$DOCROOT/includes/bootstrap.inc" 2>/dev/null || true)
fi

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — Could not detect Drupal version at $DOCROOT"
  exit 2
fi

echo "Detected Drupal version: $VERSION"

# Check if field_ui is enabled (Drupal 8+)
FIELD_UI_ENABLED="unknown"
if [ -f "$DOCROOT/sites/default/settings.php" ]; then
  # Check via core.extension.yml if it exists
  EXTENSION_FILE=$(find "$DOCROOT/sites" -path '*/sync/core.extension.yml' -o -path '*/config/core.extension.yml' 2>/dev/null | head -1)
  if [ -n "$EXTENSION_FILE" ] && [ -f "$EXTENSION_FILE" ]; then
    if grep -q 'field_ui:' "$EXTENSION_FILE" 2>/dev/null; then
      FIELD_UI_ENABLED="yes"
    else
      FIELD_UI_ENABLED="no"
    fi
  fi
  # Also check if the module directory exists and is reachable
  if [ -d "$DOCROOT/core/modules/field_ui" ]; then
    if [ "$FIELD_UI_ENABLED" = "unknown" ]; then
      FIELD_UI_ENABLED="module_present_status_unknown"
    fi
  fi
fi

# Version comparison function
version_gte() {
  [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$2" ]
}

# Determine vulnerability status based on version
VULNERABLE=false
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f1-2)

case "$MAJOR" in
  7)
    if version_gte "$VERSION" "7.107"; then
      VULNERABLE=false
    else
      VULNERABLE=true
    fi
    ;;
  8|9)
    # EOL — no patch available, always vulnerable
    VULNERABLE=true
    echo "WARNING: Drupal $MAJOR.x is end-of-life. No patch available."
    ;;
  10)
    if version_gte "$VERSION" "10.6.19"; then
      VULNERABLE=false
    elif version_gte "$VERSION" "8.9.0"; then
      VULNERABLE=true
    fi
    ;;
  11)
    if echo "$VERSION" | grep -qP '^11\.4\.'; then
      version_gte "$VERSION" "11.4.10" && VULNERABLE=false || VULNERABLE=true
    elif echo "$VERSION" | grep -qP '^11\.[0-3]\.'; then
      version_gte "$VERSION" "11.3.19" && VULNERABLE=false || VULNERABLE=true
    else
      VULNERABLE=true
    fi
    ;;
  12)
    if echo "$VERSION" | grep -q 'alpha\|beta1'; then
      VULNERABLE=true
    else
      VULNERABLE=false
    fi
    ;;
  *)
    echo "UNKNOWN — Unrecognized Drupal major version: $MAJOR"
    exit 2
    ;;
esac

if [ "$VULNERABLE" = true ]; then
  echo "field_ui module status: $FIELD_UI_ENABLED"
  if [ "$FIELD_UI_ENABLED" = "no" ]; then
    echo "PATCHED — Version $VERSION is in the vulnerable range, but field_ui is disabled (workaround active)."
    exit 0
  else
    echo "VULNERABLE — Drupal $VERSION is affected by CVE-2026-108097 (SA-CORE-2026-014)."
    echo "Remediation: Upgrade to 10.6.19 / 11.3.19 / 11.4.10 / 12.0.0-beta2+ or uninstall field_ui."
    exit 1
  fi
else
  echo "PATCHED — Drupal $VERSION is not affected by CVE-2026-108097."
  exit 0
fi