NVD has published CVE-2026-87796, a CVSS 9.8 (Critical) vulnerability in the Multi Uploader for Gravity Forms plugin for WordPress, affecting all versions up to and including 1.1.9. The flaw is an unauthenticated arbitrary file upload rooted in the move_file function, which performs insufficient file type validation during chunked upload handling. Because the attack pathway is network-reachable and requires no authentication, no privileges, and no user interaction, any internet-facing WordPress site running this plugin should be treated as an immediate exploitation target.
Arbitrary file upload in WordPress plugins is one of the most reliably weaponized vulnerability classes we see in incident response. Once an attacker can write an arbitrary file to the webroot, the path to remote code execution — typically via a PHP web shell — is trivially short. If you operate WordPress sites with Gravity Forms and this uploader add-on, treat this as a patch-or-mitigate-today event and initiate retroactive hunting for web shells.
Technical Analysis
Affected Products and Versions
| Item | Detail |
|---|---|
| Product | Multi Uploader for Gravity Forms (WordPress plugin) |
| Affected versions | All versions up to and including 1.1.9 |
| Platform | WordPress sites with the plugin installed (typically alongside Gravity Forms) |
| CVE | CVE-2026-87796 |
| CVSS | 9.8 (Critical) — network-attack vector, unauthenticated |
| Root cause | Insufficient file type validation in the move_file function during chunked upload handling |
| Reference | https://nvd.nist.gov/vuln/detail/CVE-2026-87796 |
How the Vulnerability Works (Defender's View)
The vulnerable code path lives in the plugin's chunked upload handler. Chunked uploads split a file into multiple HTTP requests, and each chunk must be reassembled and moved into a final destination. The move_file function performs that final move but fails to adequately validate the resulting file type before writing it to a location on the server's filesystem.
From an attacker's perspective, the chain looks like this:
- Identify a WordPress site running Multi Uploader for Gravity Forms ≤ 1.1.9 (plugin fingerprinting via public asset paths is trivial).
- Submit a crafted chunked upload containing a PHP payload (web shell). The chunking itself may help bypass file-type checks that only inspect the first chunk or rely on client-supplied metadata.
- The
move_filefunction reassembles and writes the payload — for example, into a directory underwp-content/uploads/that is directly reachable over HTTP. - The attacker requests the uploaded file via GET, executing the PHP payload in the web server context (typically the
www-dataor equivalent service account). - Post-exploitation: credential harvesting from
wp-config.php, database dumping, lateral movement into the hosting environment, SEO spam injection, or ransomware staging.
Exploitation Status
As of publication, NVD describes the vulnerability pathway as network-exploitable and unauthenticated. Vulnerabilities of this exact class in WordPress plugins are historically scanned for and exploited within hours to days of public disclosure, regardless of whether a public PoC exists at disclosure time. There is no confirmed CISA KEV entry at time of writing, but defenders should not wait for KEV inclusion — unauthenticated file upload to RCE in a WordPress plugin is a mass-scanning magnet. Assume active reconnaissance against your perimeter is already underway.
Detection & Response
The highest-fidelity detection opportunities are: (1) web server process writing executable files into upload directories, (2) POST requests to the plugin's upload endpoints from unusual sources, and (3) the web server process spawning child processes — the classic web-shell behavioral signature.
SIGMA Rules
---
title: Web Server Process Writing Executable File to WordPress Uploads Directory
id: 8b4e2c1a-6f3d-4a5b-9c7e-2d1f0a8b6c4d
status: experimental
description: Detects a web server or PHP process writing a PHP/script file into the WordPress uploads directory, consistent with arbitrary file upload exploitation such as CVE-2026-87796.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-87796
- https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.persistence
- attack.t1505.003
logsource:
category: file_event
product: linux
detection:
selection_process:
Image|endswith:
- '/apache2'
- '/httpd'
- '/nginx'
- '/php-fpm'
- '/php'
selection_target:
TargetFilename|contains: '/wp-content/uploads/'
TargetFilename|endswith:
- '.php'
- '.phtml'
- '.phar'
- '.php3'
- '.php4'
- '.php5'
- '.php7'
- '.pht'
condition: selection_process and selection_target
falsepositives:
- Legitimate plugin or theme installation/update performed through the WordPress admin panel
- Developer deployments to staging paths under uploads (uncommon)
level: high
---
title: Web Server Process Spawning Shell or Command Interpreter (Web Shell Behavior)
id: 3c7a9f2e-1d5b-4e8a-b6c9-0f2a4d8e1b3c
status: experimental
description: Detects web server or PHP-FPM worker processes spawning shells or common post-exploitation tools, a strong indicator of web shell execution following arbitrary file upload (e.g., CVE-2026-87796).
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-87796
- https://attack.mitre.org/techniques/T1505/003/
- https://attack.mitre.org/techniques/T1059/004/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.execution
- attack.t1505.003
- attack.t1059.004
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentImage|endswith:
- '/apache2'
- '/httpd'
- '/nginx'
- '/php-fpm'
selection_child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/zsh'
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/netcat'
- '/python'
- '/python3'
- '/perl'
- '/whoami'
- '/id'
condition: selection_parent and selection_child
falsepositives:
- Rare plugin functionality invoking system commands (e.g., image processing via exec) — investigate parent-child context
level: critical
---
title: Suspicious HTTP POST to Gravity Forms Multi Uploader Endpoint
id: 5e1b8d4c-2a7f-4c3e-8d6a-9b0c3e5f7a2d
status: experimental
description: Detects POST requests to admin-ajax.php carrying Gravity Forms multi-uploader action parameters, potentially indicating chunked upload abuse related to CVE-2026-87796. Tune the action value to your plugin's observed AJAX handler.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-87796
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.initial_access
- attack.t1190
logsource:
category: webserver
detection:
selection_uri:
cs-uri|contains:
- '/wp-admin/admin-ajax.php'
- 'admin-ajax.php'
selection_method:
cs-method: 'POST'
selection_payload:
cs-body|contains:
- 'move_file'
- 'chunk'
- 'multi_uploader'
- 'multiuploader'
- 'gform'
condition: selection_uri and selection_method and selection_payload
falsepositives:
- Legitimate form submissions using the multi-uploader field — baseline volume per source IP and flag anomalous spikes or requests from non-user geographies/ASNs
level: medium
KQL (Microsoft Sentinel / Defender)
The following hunts web access logs ingested into Sentinel (via CommonSecurityLog from a WAF/reverse proxy, or custom IIS/Apache log ingestion) for chunked upload attempts against the plugin, and separately hunts endpoint telemetry for web shell drops on Windows-hosted WordPress (IIS).
// Hunt 1: POST attempts against WordPress AJAX upload endpoints with multi-uploader characteristics
// Adjust table/columns to your log source (CommonSecurityLog for CEF-forwarded WAF/proxy logs)
CommonSecurityLog
| where TimeGenerated > ago(14d)
| where RequestMethod =~ "POST"
| where RequestURL has "admin-ajax.php"
| where AdditionalExtensions has_any ("multi_uploader", "multiuploader", "move_file", "chunk")
or RequestURL has_any ("multi_uploader", "multiuploader")
| summarize Attempts = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
by SourceIP, RequestURL, DestinationHostName
| where Attempts > 3 or FirstSeen > ago(72h)
| order by LastSeen desc;
// Hunt 2: Web server worker process creating script files in WordPress uploads (IIS-hosted WordPress on Windows)
DeviceFileEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("w3wp.exe", "php-cgi.exe", "php.exe", "httpd.exe")
| where FolderPath has @"\wp-content\uploads\"
| where FileName endswith ".php" or FileName endswith ".phtml" or FileName endswith ".aspx" or FileName endswith ".pht"
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FolderPath, FileName, SHA256, InitiatingProcessAccountName
| order by TimeGenerated desc;
// Hunt 3: Web server process spawning command interpreters (post-exploitation behavior)
DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("w3wp.exe", "php-cgi.exe", "httpd.exe")
| where FileName in~ ("cmd.exe", "powershell.exe", "pwsh.exe", "net.exe", "whoami.exe", "certutil.exe", "curl.exe")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName
| order by TimeGenerated desc;
Velociraptor VQL
Use this hunt artifact to identify PHP files recently created in WordPress uploads directories across your Linux web tier — the primary artifact left behind by this vulnerability class.
-- Hunt for recently created script files inside WordPress uploads directories
-- Indicates possible web shell deployment via arbitrary file upload (CVE-2026-87796)
LET cutoff = time() - 14*24*3600
SELECT FullPath AS FilePath,
Mtime.Sec AS ModifiedEpoch,
timestamp(epoch=Mtime.Sec) AS ModifiedTime,
Size AS FileSize,
Mode AS Permissions
FROM glob(globs=[
'/var/www/**/wp-content/uploads/**/*.php',
'/var/www/**/wp-content/uploads/**/*.phtml',
'/var/www/**/wp-content/uploads/**/*.pht',
'/var/www/**/wp-content/uploads/**/*.phar',
'/srv/www/**/wp-content/uploads/**/*.php',
'/home/**/public_html/wp-content/uploads/**/*.php'
])
WHERE Mtime.Sec > cutoff
ORDER BY Mtime.Sec DESC
Remediation / Verification Script
Run this on each WordPress host to check plugin version status, enumerate executable files in uploads directories, and apply an immediate hardening control (block PHP execution in uploads) while you patch.
#!/bin/bash
# CVE-2026-87796 - Multi Uploader for Gravity Forms verification & hardening
# Run as root or with sudo on the WordPress host
set -u
WP_ROOTS="/var/www /srv/www /home"
echo "=== [1/4] Locating Multi Uploader for Gravity Forms installations ==="
for root in $WP_ROOTS; do
[ -d "$root" ] || continue
find "$root" -type d -name "multi-uploader-for-gravity-forms" 2>/dev/null | while read -r plugindir; do
echo "FOUND plugin directory: $plugindir"
# Extract plugin version from the main plugin file header
mainfile=$(grep -rl "Version:" "$plugindir" --include="*.php" 2>/dev/null | head -1)
if [ -n "$mainfile" ]; then
ver=$(grep -m1 "Version:" "$mainfile" | awk '{print $NF}')
echo " Detected version: $ver"
# Flag vulnerable versions (1.1.9 and below)
if printf '%s\n1.1.9\n' "$ver" | sort -V -C; then
echo " STATUS: VULNERABLE (<= 1.1.9) - update or deactivate immediately"
else
echo " STATUS: appears patched (> 1.1.9) - verify against vendor advisory"
fi
fi
done
done
echo "=== [2/4] Searching uploads directories for PHP/script files (potential web shells) ==="
for root in $WP_ROOTS; do
[ -d "$root" ] || continue
find "$root" -type d -path "*/wp-content/uploads" 2>/dev/null | while read -r updir; do
hits=$(find "$updir" -type f \( -name "*.php" -o -name "*.phtml" -o -name "*.pht" -o -name "*.phar" \) -mtime -30 2>/dev/null)
if [ -n "$hits" ]; then
echo "SUSPICIOUS FILES in $updir (created/modified in last 30 days):"
echo "$hits" | while read -r f; do ls -la "$f"; done
fi
done
done
echo "=== [3/4] Checking for PHP execution blocking in uploads (.htaccess / nginx config) ==="
for root in $WP_ROOTS; do
[ -d "$root" ] || continue
find "$root" -type d -path "*/wp-content/uploads" 2>/dev/null | while read -r updir; do
if [ ! -f "$updir/.htaccess" ]; then
echo "No .htaccess in $updir - applying Apache hardening"
cat > "$updir/.htaccess" <<'EOF'
# Block PHP execution in uploads - CVE-2026-87796 mitigation
<FilesMatch "\.(php|phtml|phar|pht|php[0-9])$">
Require all denied
</FilesMatch>
EOF
chown --reference="$updir" "$updir/.htaccess" 2>/dev/null || true
echo " Wrote $updir/.htaccess"
else
echo " $updir/.htaccess exists - manually verify it blocks PHP execution"
fi
done
done
echo "=== [4/4] Reviewing web access logs for suspicious chunked upload POSTs (last 7 days) ==="
for log in /var/log/apache2/access.log /var/log/httpd/access_log /var/log/nginx/access.log; do
[ -f "$log" ] || continue
echo "--- $log ---"
grep -E "POST.*admin-ajax\.php" "$log" 2>/dev/null \
| grep -iE "multi_uploader|multiuploader|move_file|chunk" \
| awk '{print $1, $4, $6, $7}' | sort | uniq -c | sort -rn | head -20
done
echo "=== DONE. Patch the plugin via wp-admin or WP-CLI: wp plugin update multi-uploader-for-gravity-forms ==="
Remediation
- Update the plugin immediately. Upgrade Multi Uploader for Gravity Forms to the latest available release beyond 1.1.9. Verify the fixed version against the official vendor channel and the WordPress plugin repository before deploying — check the changelog explicitly references the file-upload validation fix. Monitor the NVD entry for updated version data: https://nvd.nist.gov/vuln/detail/CVE-2026-87796.
- If no patched version is available yet, deactivate and remove the plugin. With a CVSS 9.8 unauthenticated upload-to-RCE flaw, the residual risk of leaving it active — even behind a WAF — is not defensible. Remove the uploader field from live Gravity Forms and use Gravity Forms' native (patched) file upload field as a temporary substitute.
- Block PHP execution in
wp-content/uploads/as a defense-in-depth measure (Apache.htaccessor nginxlocationblock as shown in the script above). This breaks the upload-to-execution chain for this vulnerability class broadly, not just this CVE. - Hunt retroactively. Assume exploitation may predate your patch. Sweep uploads directories for unexpected PHP/script files (see VQL and script above), review web access logs for anomalous POST volume to
admin-ajax.php, and inspect the web server account for spawned child processes or outbound connections. - If any web shell artifacts are found, treat it as a full compromise: isolate the host, preserve forensic images, rotate all credentials in
wp-config.php(database, salts/keys, admin accounts), audit WordPress user accounts for rogue administrators, and rebuild from a known-clean backup where integrity cannot be assured. - Reduce attack surface going forward: enforce automatic updates for WordPress plugins, inventory all installed plugins across your WordPress estate (a forgotten plugin is an unmanaged vulnerability), and place a WAF rule set with virtual patching capability in front of internet-facing WordPress properties.
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.