Back to Intelligence

CVE-2026-126537: Critical WordPress Ninja Forms File Uploads Arbitrary File Deletion — Detection and Remediation Guide

SA
Security Arsenal Team
November 11, 2026
11 min read

On the surface, a file upload add-on for a WordPress forms plugin sounds like low-stakes software. In practice, it is anything but. NVD has published CVE-2026-126537, a CVSS 9.1 (Critical), network-exploitable vulnerability in the Ninja Forms – File Uploads add-on for WordPress, affecting all versions up to and including 3.3.36. The flaw enables arbitrary file deletion and, through the plugin's S3, Dropbox, and Google Drive integrations, arbitrary file read with external exfiltration — without authentication.

For defenders, the worst-case scenarios write themselves: deletion of wp-config.php can force a site into its installation workflow (a classic database-takeover primitive), deletion of .htaccess can strip server-level access controls, and reading wp-config.php hands an attacker database credentials and authentication salts. WordPress powers a substantial share of the public web, and Ninja Forms has a large installed base — if you host, manage, or monitor WordPress properties, this belongs at the top of your patch queue this week.

Technical Analysis

Affected Product and Versions

  • Product: Ninja Forms – File Uploads add-on for WordPress
  • Affected versions: All versions up to and including 3.3.36
  • Platform: WordPress (PHP), any hosting OS — the vulnerable code is in the plugin itself
  • CVE: CVE-2026-126537 — CVSS 3.1 score 9.1 (Critical), attack vector NETWORK, no privileges required, no user interaction
  • Reference: https://nvd.nist.gov/vuln/detail/CVE-2026-126537

Root Cause: Two Logic Flaws Chained by Design

The vulnerability is not a memory-corruption bug — it is a pair of trust-boundary logic errors in how the plugin reconciles client-supplied upload state with server-side authoritative state. Understanding both halves matters for detection, because each produces distinct telemetry.

Flaw 1 — NF_FU_Fields_Upload::process() in includes/fields/upload.php. When the plugin processes a public form submission, it checks for a saveProgress value in the extra parameter. If extra.saveProgress is non-empty, the function returns early — skipping the server-side validation/sanitization pass that would normally reconcile the uploaded-file list. Critically, the early return leaves the client-supplied files[] array intact. In other words: an attacker who submits a form with any saveProgress value gets to dictate, verbatim, the list of file paths the plugin believes it uploaded. Those paths are not restricted to the upload directory — the plugin trusts them as authoritative file references.

Flaw 2 — NF_FU_External_Abstracts_Service::process_upload() in includes/external/abstracts/service.php. This is the shared abstract class behind the S3, Dropbox, and Google Drive services. After an upload is processed, the service is supposed to reload the file path from the authoritative server-side record before acting on it. But that reload only fires when the client sends an upload_id in the request. By omitting upload_id entirely, the attacker bypasses the authoritative-path reload — so the external service operates directly on the attacker-controlled path from flaw 1.

Exploitation Chain (Defender's View)

  1. Attacker identifies a WordPress site with Ninja Forms + File Uploads ≤ 3.3.36 and a published form with a file upload field.
  2. Attacker submits the form (no authentication needed on public forms) with extra[saveProgress] set to any non-empty value and a files[] entry pointing at a target path, e.g. wp-config.php, .htaccess, or any readable file on the webroot.
  3. The upload field's process() returns early, preserving the attacker-controlled path.
  4. The attacker omits upload_id, so the external service never reloads the authoritative path.
  5. Outcome A — deletion: the plugin's cleanup/unlink routine deletes the attacker-specified file from disk. Outcome B — read/exfiltration: if an external storage service (S3/Dropbox/Google Drive) is configured, the plugin uploads the targeted file to external storage, achieving arbitrary file read with built-in exfiltration over legitimate HTTPS to trusted cloud domains.

Exploitation Status

As of publication, the CVE has been documented by NVD with full technical detail on the vulnerable code paths — that level of detail typically correlates with a researcher write-up being publicly available, which materially shortens the time-to-exploit for mass scanners. WordPress plugin vulnerabilities of this class (unauthenticated, file-system impact) are historically among the fastest to be weaponized after disclosure. Treat this as exploitable now even if CISA KEV inclusion has not yet been confirmed; do not wait for confirmed in-the-wild activity to patch.

Detection & Response

What to Look For

The highest-fidelity signals live in your web server access logs and WAF telemetry, not the endpoint:

  • POST requests to WordPress form submission endpoints (commonly admin-ajax.php, admin-post.php, or REST routes such as /wp-json/) containing both saveProgress and files parameters in the body — especially from sources with no prior session history.
  • File upload plugin endpoints hit without an upload_id parameter where one is normally present (legitimate multi-step uploads send it).
  • Web server process unlinking or reading files outside the Ninja Forms upload directory (wp-content/uploads/ninja-forms/).
  • Outbound HTTPS from the web server to *.amazonaws.com, api.dropboxapi.com, or googleapis.com/upload immediately following suspicious form submissions.

Sigma Rules

YAML
---
title: Ninja Forms File Uploads saveProgress Abuse in Form Submission (CVE-2026-126537)
id: 8c1e4a72-5b3d-4f19-a6c7-2d9e1f0b3a55
status: experimental
description: Detects HTTP POST bodies to WordPress form endpoints containing both saveProgress and files[] parameters, the manipulation pattern used to bypass server-side file list validation in Ninja Forms File Uploads <= 3.3.36.
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-126537
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.initial_access
  - attack.t1190
logsource:
  category: webserver
  product: apache
detection:
  selection_uri:
    cs-uri-stem|contains:
      - '/wp-admin/admin-ajax.php'
      - '/wp-admin/admin-post.php'
      - '/wp-json/'
  selection_method:
    cs-method: 'POST'
  selection_body:
    cs-body|contains:
      - 'saveProgress'
      - 'files%5B'
      - 'files['
  condition: selection_uri and selection_method and 1 of selection_body
falsepositives:
  - Legitimate users saving partial form progress via the Save Progress feature
level: high
---
title: Web Server Process Deleting Files Outside WordPress Upload Directory
id: 3f7b9c14-8e2a-4d56-b1c9-5a7f3e0d6c21
status: experimental
description: Detects the web server/PHP-FPM process unlinking sensitive files such as wp-config.php or .htaccess, consistent with arbitrary file deletion via CVE-2026-126537 in Ninja Forms File Uploads.
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-126537
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.impact
  - attack.t1485
logsource:
  category: file_delete
  product: linux
detection:
  selection_user:
    user|contains:
      - 'www-data'
      - 'apache'
      - 'nginx'
  selection_target:
    TargetFilename|endswith:
      - 'wp-config.php'
      - '.htaccess'
      - 'wp-settings.php'
      - 'wp-load.php'
  condition: selection_user and selection_target
falsepositives:
  - Deployment pipelines running as the web user (rare and should be reconciled)
level: critical
---
title: Suspicious Outbound Cloud Storage Exfiltration From Web Server Process
id: 6d2a8f51-1c47-4b93-9e35-7b0d4f2a8e66
status: experimental
description: Detects outbound connections from web server or PHP-FPM processes to S3, Dropbox API, or Google Drive upload endpoints, the exfiltration channel used by CVE-2026-126537 to read arbitrary files via external storage services.
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-126537
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.exfiltration
  - attack.t1567.002
logsource:
  category: network_connection
  product: linux
detection:
  selection_proc:
    Image|endswith:
      - '/php-fpm'
      - '/apache2'
      - '/httpd'
      - '/php'
  selection_dst:
    DestinationHostname|contains:
      - 'amazonaws.com'
      - 'dropboxapi.com'
      - 'googleapis.com'
  condition: selection_proc and selection_dst
falsepositives:
  - Legitimate Ninja Forms File Uploads S3/Dropbox/Google Drive integration traffic (baseline your enabled integrations and alert on deviations)
level: medium

A note on tuning: the first rule will fire on legitimate use of Ninja Forms' Save Progress feature. That is acceptable during the patch window — the goal is to enumerate which forms and source IPs touch this code path, then tighten once 3.3.37+ is deployed.

KQL (Microsoft Sentinel / Defender)

This hunt assumes your Apache/Nginx access logs flow into Sentinel (via the CEF/Syslog connector or a custom CommonSecurityLog/custom table mapping), and that web server hosts are onboarded to Defender for Endpoint.

KQL — Microsoft Sentinel / Defender
// Hunt 1: Form submissions carrying saveProgress + files[] to WordPress endpoints
CommonSecurityLog
| where TimeGenerated > ago(14d)
| where RequestMethod =~ "POST"
| where RequestURL has_any ("admin-ajax.php", "admin-post.php", "/wp-json/")
| where AdditionalExtensions has "saveProgress"
   or Message has "saveProgress"
| extend HasFiles = Message has "files["
| project TimeGenerated, SourceIP, RequestURL, HasFiles, Message
| where HasFiles
| order by TimeGenerated desc
;
// Hunt 2: PHP-FPM/apache processes establishing outbound connections to cloud storage APIs
DeviceNetworkEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessName in~ ("php-fpm", "apache2", "httpd", "php")
| where RemoteUrl has_any ("amazonaws.com", "dropboxapi.com", "googleapis.com")
| summarize Connections = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
    by DeviceName, InitiatingProcessName, RemoteUrl, RemoteIP
| order by Connections desc
;
// Hunt 3: Sensitive WordPress files deleted under the web service account context
DeviceFileEvents
| where TimeGenerated > ago(14d)
| where ActionType == "FileDeleted"
| where FileName has_any ("wp-config.php", ".htaccess", "wp-settings.php")
| project TimeGenerated, DeviceName, FolderPath, FileName, InitiatingProcessName, InitiatingProcessAccountName
| order by TimeGenerated desc

Velociraptor VQL

Use this artifact across Linux web server fleets to (a) inventory vulnerable plugin versions and (b) surface PHP-FPM processes talking to external storage services — both live indicators for this CVE.

VQL — Velociraptor
-- CVE-2026-126537: inventory Ninja Forms File Uploads versions and flag external storage connections
LET plugin_files <= SELECT FullPath, Mtime
FROM glob(globs='/var/www/**/wp-content/plugins/ninja-forms-uploads*/readme.txt')

LET cloud_conns <= SELECT Pid, Name, Family, Type, Status, Laddr, Raddr
FROM netstat()
WHERE Name =~ 'php|apache|httpd|nginx'
  AND Raddr.IP =~ '.'

SELECT * FROM plugin_files
UNION ALL
SELECT NULL AS FullPath, NULL AS Mtime, Pid, Name, Raddr.IP AS RemoteIP, Status
FROM cloud_conns

For point-in-time verification of a single host, the Bash script below is faster and simpler.

Remediation / Verification Script

Run this on each WordPress host (adjust WP_ROOT and web user as needed). It inventories the plugin version, flags the vulnerable code paths, and greps recent access logs for the exploitation pattern.

Bash / Shell
#!/usr/bin/env bash
# CVE-2026-126537 verification helper - Ninja Forms File Uploads <= 3.3.36
WP_ROOT="${1:-/var/www/html}"
PLUGIN_DIR=$(find "$WP_ROOT" -maxdepth 5 -type d -iname 'ninja-forms-uploads*' 2>/dev/null)

if [ -z "$PLUGIN_DIR" ]; then
  echo "[OK] Ninja Forms File Uploads add-on not found under $WP_ROOT"
else
  echo "[FOUND] Plugin directory: $PLUGIN_DIR"
  grep -riE "Version:" "$PLUGIN_DIR"/*.php 2>/dev/null | head -n 3
  for d in $PLUGIN_DIR; do
    ver=$(grep -m1 -riE '^[[:space:]]*\*?[[:space:]]*Version:' "$d"/*.php 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
    echo "[INFO] Detected version: ${ver:-unknown} in $d"
    if [ -n "$ver" ]; then
      if [ "$(printf '%s\n3.3.36' "$ver" | sort -V | head -n1)" != "3.3.36" ] && [ "$ver" != "3.3.37" ]; then
        echo "[ALERT] $ver is <= 3.3.36 - VULNERABLE to CVE-2026-126537. Update immediately."
      else
        echo "[OK] Version $ver appears patched (>= 3.3.37)."
      fi
    fi
    # Confirm vulnerable logic presence
    grep -l 'saveProgress' "$d/includes/fields/upload.php" 2>/dev/null && echo "[INFO] saveProgress code path present in upload.php"
  done
fi

# Hunt recent access logs for exploitation pattern: saveProgress + files in POST bodies
echo "--- Access log triage (last 7 days of gzipped/plain logs if zgrep available) ---"
for log in /var/log/apache2/access*.log /var/log/nginx/access*.log /var/log/httpd/access*.log; do
  [ -f "$log" ] || continue
  grep -aE 'POST .*(admin-ajax|admin-post|wp-json)' "$log" 2>/dev/null \
    | grep -ai 'saveProgress' | tail -n 20 && echo "[REVIEW] Suspicious submissions in $log"
done

# Check for outbound connections to cloud storage from web processes right now
echo "--- Live outbound connections from web/PHP processes ---"
ss -tnp 2>/dev/null | grep -E 'php-fpm|apache2|httpd|nginx' | grep -Ei 'amazonaws|dropbox|googleapis' || echo "[OK] No live cloud-storage connections from web processes."

Remediation

1. Patch immediately. Update the Ninja Forms – File Uploads add-on to the fixed release newer than 3.3.36 (pull the current version from the vendor via your WordPress admin dashboard or WP-CLI: wp plugin update <plugin-slug>). Verify the update landed with wp plugin list and confirm the running version on every site — multisite networks and staging clones are frequently missed and remain exploitable.

2. Disable external storage integrations until patched. If you cannot patch within hours, disable the S3, Dropbox, and Google Drive upload destinations in the Ninja Forms File Uploads settings. This severs the arbitrary-file-read/exfiltration leg of the chain. It does not close the arbitrary-file-deletion leg — patching is still mandatory.

3. Consider disabling the Save Progress feature. The extra.saveProgress early-return is the entry point of the chain. Disabling save-progress functionality reduces attack surface while you roll out the patch across a fleet.

4. Add a compensating WAF rule. Where patching is delayed, deploy a WAF rule that blocks or challenges POST bodies to admin-ajax.php, admin-post.php, and /wp-json/ routes containing saveProgress combined with files[ parameters sourced from IPs with no established session cookie. Treat this as a temporary control — virtual patching is not a substitute for the vendor fix.

5. Post-patch verification and retro-hunt. After updating, run the detection content above against at least 14 days of historical logs. Look for the saveProgress+files[] pattern, deletions of wp-config.php/.htaccess, and unexpected outbound transfers to cloud storage APIs. If any of these fire, escalate to incident response: arbitrary file read of wp-config.php means database credentials and WordPress auth keys/salts must be rotated, and you should audit for new administrator accounts, modified core/plugin files, and webshells dropped elsewhere in the webroot.

6. Longer-term hardening. Enforce least-privilege filesystem permissions so the web server user cannot write or delete outside wp-content/uploads; keep DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS set in wp-config.php; and ensure your vulnerability-management program has a defined SLA for critical WordPress plugin CVEs — this class of bug recurs constantly in the WordPress ecosystem and needs a standing playbook, not ad-hoc response.

Advisory reference: https://nvd.nist.gov/vuln/detail/CVE-2026-126537

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.