← Back to Feed CACHED · 2026-10-07 20:16:02 · CACHE_KEY CVE-2026-15748
CVE-2026-15748 · CWE-434 · Disclosed 2026-08-18

Forminator Forms ≤ 1.56.1 Unauthenticated Arbitrary File Upload via Forged Select Field Configuration

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

Someone left the loading dock unlocked, and the security camera only covers half the lot

CVE-2026-15748 is an unauthenticated arbitrary file upload in the Forminator Forms WordPress plugin (≤ 1.56.1, 600,000+ active installs). The handle_file_upload() function uses an exact-key blocklist to reject dangerous extensions, but attackers bypass it by submitting the MIME key ph(p)|text/x-php — not an exact match for php, yet WordPress's internal matcher still resolves it to .php. Worse, the public submission handler trusts attacker-controlled configuration injected via a forged Select field value, letting an unauthenticated visitor write a PHP webshell to the uploads directory. The flaw was reported by researcher daroo through the Wordfence Bug Bounty Program on July 11, 2026, patched in 1.56.2 on July 31, and publicly disclosed August 18.

The vendor's CVSS 9.8 CRITICAL is honest about the *theoretical* ceiling — unauthenticated, no interaction, full CIA impact — but it overestimates the *default* exploitability on Apache/LiteSpeed hosts. Forminator ships an .htaccess file in wp-content/uploads/forminator/ that blocks PHP execution, so on a stock Apache setup the uploaded shell is inert even after a successful upload. That protection vanishes in two common scenarios: (1) the site runs nginx, which ignores .htaccess entirely (~30-40% of WordPress hosting), and (2) the administrator configured a custom upload storage root, which may lack the protective .htaccess because it's first initialised during a frontend request where the WordPress helper that creates it isn't loaded. Factoring in the form-configuration prerequisite (both File Upload *and* Select fields must be present) and the Apache default mitigation, a reassessed score of 9.0 CRITICAL better reflects the real-world attack surface while still flagging this as a drop-everything-and-patch issue.

"Unauth RCE on 600K WordPress sites — Apache .htaccess helps but nginx is wide open"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Target discovery

The attacker scans for WordPress installations running Forminator by fingerprinting wp-content/plugins/forminator/ asset paths and readme.txt version strings. Tools like WPScan, Nuclei, and the purpose-built yora1928/cve-2026-15748 scanner automate this at scale across IP ranges or domain lists. Version ≤ 1.56.1 is flagged as exploitable.
Conditions required:
  • Target runs WordPress with Forminator ≤ 1.56.1
  • Plugin assets or readme.txt are publicly accessible
Where this breaks in practice:
  • Sites behind Cloudflare or similar CDN may not expose plugin paths directly
  • Some hosts strip version strings from plugin assets
Detection/coverage: WPScan, Nuclei nuclei-wordfence-cve templates, Qualys WAS, Tenable WAS all detect the installed version.
STEP 02

Form reconnaissance

The attacker crawls the target site — sitemaps, common page slugs, search — looking for a rendered Forminator form that contains both a File Upload field and a Select (dropdown) field. The scanner extracts the form ID and the nonce token from the page HTML. Both values are required for a valid submission to admin-ajax.php.
Conditions required:
  • A public page contains a Forminator form with File Upload + Select fields
  • The nonce token is embedded in the page source
Where this breaks in practice:
  • Not every Forminator form has both field types; simple contact forms with only text fields are not exploitable
  • Forms behind authentication (member-only pages) narrow the target pool
Detection/coverage: WAF logs showing page-crawling patterns followed by POST to admin-ajax.php; anomalous GET volume on form pages.
STEP 03

Payload injection via forged Select field

The attacker crafts a multipart POST to admin-ajax.php with the Forminator submission action. Inside the Select field's nested value structure, they inject a fake record containing field_type=upload, a custom name, and an attacker-controlled additional-type string set to ph(p)|text/x-php. The set_field_data() function processes this without verifying origin, adding the forged upload config to the internal field_data_array.
Conditions required:
  • Attacker can submit POST requests to /wp-admin/admin-ajax.php
  • Valid form ID and nonce are known
Where this breaks in practice:
  • WAFs with WordPress-aware rulesets (Wordfence Premium, Sucuri, Cloudflare WAF) may flag the nested array injection pattern
  • Request body containing php-related MIME strings may trigger signature-based detection
Detection/coverage: SIGMA rule: POST to admin-ajax.php with forminator action containing nested field_type=upload in Select field parameters.
STEP 04

PHP file upload via MIME type bypass

The process_uploads() function encounters the injected record and treats it as a legitimate upload field configuration. The blocklist checks the MIME key against known dangerous extensions using exact matching — ph(p)|text/x-php does not match the key php, so the check passes. WordPress's wp_check_filetype() then resolves the ph(p) pattern back to .php, and the file is written to the uploads directory with a PHP extension. The attacker's webshell is now on disk.
Conditions required:
  • The MIME type bypass is not caught by server-level validation
  • File is written to a web-accessible directory
Where this breaks in practice:
  • On Apache with AllowOverride enabled (the default for most cPanel/Plesk WordPress hosts), the .htaccess file in wp-content/uploads/forminator/ blocks PHP execution — the file lands but cannot be executed
  • Hardened PHP configurations with open_basedir or disable_functions limit post-upload impact
Detection/coverage: File integrity monitoring (OSSEC, Wazuh, Tripwire) detecting new .php files in upload directories; Velociraptor VQL glob for wp-content/uploads/**/*.php.
STEP 05

Webshell execution and RCE

The attacker sends a GET request to the uploaded PHP file (e.g., https://target/wp-content/uploads/forminator/random-prefix-shell.php). If the web server executes PHP in this directory — as is the case on nginx by default, Apache with AllowOverride disabled, or sites using a custom upload storage root lacking .htaccess — the webshell runs as the web server user (www-data/apache/nginx). From here, the attacker reads wp-config.php for DB credentials, adds admin users, installs backdoors, or pivots.
Conditions required:
  • Web server executes PHP in the uploads directory (nginx, misconfigured Apache, custom upload path)
  • Uploaded file is web-accessible
Where this breaks in practice:
  • Apache + default .htaccess: PHP execution is blocked — chain dies here for ~60% of installs
  • Managed WordPress hosts (WP Engine, Kinsta, Flywheel) typically deny PHP execution in uploads at the platform level
  • Some nginx configs include deny rules for PHP in wp-content/uploads
Detection/coverage: IDS/WAF alerting on GET requests for .php files in upload paths; web server child-process monitoring (PHP-FPM spawning bash, curl, wget, nc).
03 · Compensating Control

1
CRITICAL 9.0→IGNORE 0.0
SEVERITY REDUCED
Update Forminator to 1.56.2 immediately — The only complete fix. Deploy within 3 days per the noisgate mitigation SLA for CRITICAL. Use wp plugin update forminator or enable WordPress auto-updates for the plugin. Verify post-update with the version check script. This eliminates the vulnerability entirely.
2
CRITICAL 9.0→MEDIUM 4.0
SEVERITY REDUCED
Block PHP execution in all upload directories at the web server level — For nginx: add location ~* /wp-content/uploads/.*\.php$ { deny all; } to your server block. For Apache: ensure wp-content/uploads/.htaccess contains <FilesMatch "\.(php|phtml|phar|php[0-9])$">Require all denied</FilesMatch>. Deploy within 3 days. This breaks the RCE chain at step 5 — the shell uploads but cannot execute — regardless of Forminator version.
3
CRITICAL 9.0→HIGH 7.0
SEVERITY REDUCED
Deploy WAF rule blocking PHP content in Forminator submissions — Add a rule to your WAF (Cloudflare, Sucuri, ModSecurity, Wordfence) blocking multipart POST requests to admin-ajax.php where the body contains field_type=upload in non-file-upload field parameters, or where MIME type strings contain php, phtml, or phar patterns. Deploy within 3 days. This blocks the injection at step 3 of the attack chain.
4
CRITICAL 9.0→IGNORE 0.0
SEVERITY REDUCED
Deactivate Forminator if not actively used — Run wp plugin deactivate forminator on any site where the plugin is installed but not required. Many WordPress installations accumulate unused plugins. Deactivation removes the vulnerable endpoint entirely. Deploy within 3 days.
5
CRITICAL 9.0→IGNORE 0.0
SEVERITY REDUCED
Remove File Upload fields from public forms — If you need Forminator active but don't require file uploads, remove File Upload fields from all public-facing forms. This eliminates the prerequisite for exploitation without disabling the plugin. Verify by checking each form's field configuration in the Forminator dashboard.
What doesn't work
  • WordPress login protection / admin authentication — the vulnerable endpoint admin-ajax.php handles unauthenticated requests by design; requiring login on /wp-admin/ does not block the attack path.
  • File-rename or randomize-filename plugins — the MIME type bypass operates at the validation layer *before* any filename transformation; the file still lands with a .php extension regardless of name randomization.
  • Rate limiting on admin-ajax.php — a single POST request is sufficient for full exploitation; throttling does not prevent the attack, only slows mass scanning.
  • WordPress nonce verification alone — the nonce is extracted from the public form page by the attacker's scanner; it is not a secret when the form is publicly rendered.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNo confirmed active exploitation. CISA classified the attack as "automatable" but reported exploitation status as "none" at disclosure (2026-08-18). No KEV listing. However, 51 days post-disclosure with public PoCs — opportunistic scanning for this class of WordPress vuln is virtually certain. No campaign-specific IOCs published.
Proof-of-concept availabilityMultiple public PoCs. GitHub: yora1928/cve-2026-15748 (automated scanner with nonce extraction and safe upload test, by researcher Yora), ubaydev/CVE-2026-15748 (full exploit). Nuclei templates available via topscoder/nuclei-wordfence-cve. Exploitation barrier is effectively zero.
EPSS0.06138 (6.14%) — places this well above the median and in approximately the top 5th percentile of scored CVEs. Consistent with the unauthenticated-RCE-in-popular-plugin profile.
KEV statusNot listed as of 2026-10-08. Absence does not indicate safety — KEV lags real-world exploitation for WordPress plugin vulns.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Network attack vector, low complexity, no privileges, no user interaction, unchanged scope, all three CIA impacts high. This is the maximum-severity profile for a single-component vuln.
Affected versionsForminator Forms ≤ 1.56.1 (all versions). Plugin has 600,000+ active installations per WordPress.org; approximately 300,000 remain unpatched based on download telemetry reported by SecurityWeek.
Fixed versions1.56.2 (released July 31, 2026). Available via WordPress Dashboard auto-update or wp plugin update forminator. No distro backports — this is a WordPress plugin, not a system package.
Scanning / exposureNo specific GreyNoise tags or Shodan dorks published for this CVE as of reporting. FOFA/Censys queries for forminator in WordPress plugin paths return hundreds of thousands of results. The yora1928 scanner tool enables mass reconnaissance.
Disclosure timelineReported to Wordfence Bug Bounty: 2026-07-11. Patch released: 2026-07-31 (20-day turnaround). Public disclosure: 2026-08-18. Days since disclosure: 51.
Reporting researcherdaroo, via the Wordfence Bug Bounty Program. Technical writeup published by The Hacker News and SOC Prime.

Sources.

  1. The Hacker News — Forminator RCE Analysis
  2. SecurityWeek — 300,000 WordPress Sites Exposed
  3. SecurityArsenal — Detection, Hunting, and Remediation Guide
  4. SOC Prime — CVE-2026-15748 Analysis
  5. DEV Community — Technical Root Cause Writeup
  6. GitHub PoC — yora1928/cve-2026-15748
  7. GitHub PoC — ubaydev/CVE-2026-15748
  8. NVD — CVE-2026-15748 Detail
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.0/10)

Why this verdict

  • Unauthenticated remote RCE with zero interaction: The attack vector is as bad as it gets — network-reachable, no authentication, no user click, low complexity. Any internet-facing Forminator form with the right field combination is a target. This alone anchors the score in CRITICAL territory.
  • Public weaponized tooling eliminates skill barrier: Two GitHub PoC repos and Nuclei templates mean any script kiddie with a domain list can mass-scan and exploit. The yora1928 scanner automates the entire chain including form crawling, nonce extraction, and upload testing. Exploitation is point-and-shoot.
  • nginx exposure gap (~30-40% of WordPress hosting): The default .htaccess protection ONLY works on Apache and LiteSpeed. On nginx — which powers a substantial minority of WordPress deployments including many high-traffic sites — .htaccess is completely ignored. Uploaded PHP files execute immediately. This means the 'default mitigation' protects roughly 60% of the install base, leaving ~100,000+ unpatched sites fully exploitable.
  • Apache .htaccess default protection (downward -0.4): On stock Apache/LiteSpeed configurations (cPanel, Plesk, most shared hosting), Forminator places an .htaccess in the uploads directory that denies PHP execution. The uploaded shell lands but is inert. This genuinely breaks the chain for the majority of WordPress hosting configurations.
  • Form configuration prerequisite (downward -0.4): Exploitation requires a form with *both* File Upload and Select fields. While this combination is common in application/registration/support forms, simple contact-us forms with only text inputs are not exploitable. This provides moderate but real friction.
  • Role multiplier: WordPress is a web application, not a canonically high-value infrastructure component. (a) *Low-value role*: personal blogs, dev staging sites — compromise yields defacement/spam, minimal blast radius. (b) *Typical role*: corporate marketing site, customer portal, small ecommerce — compromise yields webshell + database (PII, credentials), host-level blast radius. (c) *High-value role*: WooCommerce store processing payments, enterprise intranet, multisite network hosting dozens of tenants — compromise yields payment data, session tokens, and potential lateral movement into the corporate network. For an enterprise managing 10K hosts with 5-20 WordPress instances, these are typically internet-facing and thus directly exposed. The blast radius per-instance is host+database, not domain/fleet/supply-chain. WordPress does not meet the canonical high-value-role threshold (not a DC, hypervisor, IdP, or CI/CD), so no floor override applies — but the unauthenticated-RCE-from-internet profile independently sustains CRITICAL.

Why not higher?

The score is already CRITICAL. The two friction points — Apache .htaccess default protection breaking the RCE chain for ~60% of installations, and the form-configuration prerequisite eliminating forms without File Upload + Select fields — provide genuine, measurable narrowing of the exploitable population. A 9.8 would imply every Forminator instance is trivially exploitable, which is not the case for stock Apache deployments.

Why not lower?

Unauthenticated remote RCE with public exploit tooling on 600,000+ internet-facing installations is the textbook definition of CRITICAL. The nginx exposure alone puts ~100,000+ unpatched sites at immediate risk with zero additional conditions beyond finding the right form. EPSS of 6.14% confirms elevated exploitation probability. Dropping to HIGH would require evidence that the exploitable fraction is negligibly small, which the nginx install-base data directly contradicts. The 51-day window since disclosure with no patch adoption mandate makes this a live, active-surface issue.

06 · Verification

Crowdsourced verification payload.

Run this script on each WordPress host (or against a mounted filesystem) as any user with read access to wp-content/plugins/. Usage: bash cve-2026-15748-check.sh /var/www/html. No elevated privileges required for the version check; nginx detection uses pgrep which may need appropriate permissions.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/bin/bash
# CVE-2026-15748 — Forminator Forms Arbitrary File Upload Checker
# Outputs: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -uo pipefail

WP_ROOT="${1:-/var/www/html}"
VULN_CEILING="1.56.1"
FIXED="1.56.2"
PLUGIN_FILE="$WP_ROOT/wp-content/plugins/forminator/forminator.php"

if [ ! -f "$PLUGIN_FILE" ]; then
  echo "UNKNOWN — Forminator plugin not found at $WP_ROOT"
  exit 2
fi

# Extract version from plugin header
VERSION=$(grep -iP '^\s*\*\s*Version:\s*' "$PLUGIN_FILE" 2>/dev/null \
  | head -1 | sed 's/.*[Vv]ersion:\s*//;s/[[:space:]]*$//')

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — could not parse Forminator version from $PLUGIN_FILE"
  exit 2
fi

echo "[*] Forminator version detected: $VERSION"

# Version comparison: is $VERSION <= $VULN_CEILING?
HIGHEST=$(printf '%s\n%s' "$VERSION" "$VULN_CEILING" | sort -V | tail -1)
if [ "$HIGHEST" = "$VULN_CEILING" ] || [ "$VERSION" = "$VULN_CEILING" ]; then
  # Additional risk assessment
  echo "[!] VULNERABLE — Forminator $VERSION <= $VULN_CEILING (CVE-2026-15748)"
  echo "    Fix: upgrade to >= $FIXED"

  # Check web server type
  if command -v nginx >/dev/null 2>&1 && pgrep -x nginx >/dev/null 2>&1; then
    echo "    [CRITICAL RISK] nginx detected — .htaccess does NOT prevent PHP execution in uploads"
  fi

  # Check .htaccess in uploads
  UPLOAD_DIR="$WP_ROOT/wp-content/uploads/forminator"
  if [ -d "$UPLOAD_DIR" ]; then
    if [ -f "$UPLOAD_DIR/.htaccess" ]; then
      echo "    [MITIGATED on Apache] .htaccess found in uploads dir — PHP execution blocked on Apache/LiteSpeed"
    else
      echo "    [HIGH RISK] No .htaccess in $UPLOAD_DIR — PHP execution likely possible"
    fi
  fi

  # Check for existing webshells
  if [ -d "$UPLOAD_DIR" ]; then
    SHELLS=$(find "$UPLOAD_DIR" -name '*.php' -o -name '*.phtml' -o -name '*.phar' 2>/dev/null | head -5)
    if [ -n "$SHELLS" ]; then
      echo "    [ALERT] Suspicious PHP files found in uploads directory:"
      echo "$SHELLS" | while read -r f; do echo "      $f"; done
    fi
  fi

  exit 1
else
  echo "[+] PATCHED — Forminator $VERSION is not affected by CVE-2026-15748"
  exit 0
fi