A critical vulnerability in the Gravity Forms plugin for WordPress exposes millions of websites to unauthenticated remote code execution. Security teams running WordPress sites with this popular form builder must act immediately to patch and verify their defenses before mass exploitation campaigns begin.
Introduction
On February 9, 2026, the National Vulnerability Database (NVD) published CVE-2026-103039, assigning it a CVSS score of 9.8 (Critical). This vulnerability affects all versions of Gravity Forms up to and including 3.1.2, one of the most widely deployed form builder plugins for WordPress with over 5 million active installations.
The flaw is a classic PHP Object Injection vulnerability that allows unauthenticated attackers to execute arbitrary PHP code on the server by exploiting how Gravity Forms handles List-type fields in post submissions. The vulnerability pathway is NETWORK, meaning any remote attacker can exploit this without any authentication or user interaction. Given the popularity of Gravity Forms and the trivial nature of exploitation, we expect to see mass scanning and active exploitation within days of public disclosure.
What makes this particularly dangerous is that many organizations don't realize they're running Gravity Forms. It's often bundled with themes, installed as part of site development projects, or inherited from previous administrators. The plugin's extensive feature set—including its Post Custom Field functionality that enables user-generated content—makes it a prime target for threat actors seeking to compromise WordPress infrastructure.
Technical Analysis
Affected Products and Versions
Product: Gravity Forms plugin for WordPress
Affected Versions: All versions up to and including 3.1.2
Patched Version: 3.1.3 (released February 8, 2026)
Component: GFFormsModel::create_post() function
Attack Vector: Network (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
The vulnerability specifically affects forms that use:
- Post Custom Field with input type set to "List"
- Content template enabled on that field
When both conditions are met, the submitted value flows through to the vulnerable code path without proper sanitization.
Vulnerability Root Cause
The vulnerability stems from a deserialization flaw in the GFFormsModel::create_post() function. When processing form submissions containing Post Custom Fields with List input type, the code passes the submitted value directly to WordPress's global maybe_unserialize() function instead of using Gravity Forms' own hardened GFCommon::maybe_unserialize() method.
Here's the critical difference:
Vulnerable Code Path (GFFormsModel):
$custom_field_value = maybe_unserialize( $submitted_value );
Hardened Alternative (GFCommon, added in 3.1.1):
$custom_field_value = GFCommon::maybe_unserialize( $submitted_value );
The WordPress core maybe_unserialize() function performs unrestricted deserialization without any object class filtering. In contrast, GFCommon::maybe_unserialize() was specifically hardened in version 3.1.1 to pass allowed_classes => false to the underlying unserialize() call, preventing any PHP object instantiation during deserialization.
When a Post Custom Field with List type has a content template enabled, the submitted row reaches the vulnerable maybe_unserialize() call verbatim, allowing an attacker to inject a serialized PHP object payload.
Exploitation Chain
From a defender's perspective, the attack chain works as follows:
- Reconnaissance: Attacker identifies WordPress sites running Gravity Forms (detectable via plugin file paths, form markup, or version enumeration)
- Target Verification: Locates forms containing Post Custom Fields with List input type and content templates
- Payload Crafting: Constructs a serialized PHP object payload that exploits a POP (Property-Oriented Programming) chain in a loaded library
- Delivery: Submits the malicious payload via the vulnerable form field (unauthenticated POST request)
- Deserialization: Server unserializes the payload, instantiating arbitrary PHP objects
- Code Execution: POP chain triggers
__wakeup(),__destruct(), or other magic methods, leading to remote code execution
Requirements for Successful Exploitation:
- Gravity Forms ≤3.1.2 installed and activated
- At least one form with Post Custom Field using List input type
- Content template enabled on that field
- A suitable POP chain available in the WordPress environment (typically through other plugins or WordPress core classes)
Exploitation Status
As of February 9, 2026, this vulnerability has not been added to CISA's Known Exploited Vulnerabilities (KEV) catalog. However, given the critical severity, unauthenticated nature, and the fact that Gravity Forms is one of the most popular WordPress plugins, we assess that active exploitation is imminent.
Security researchers have noted that the Wordfence threat intelligence team is already observing scanning activity targeting Gravity Forms installations. Public proof-of-concept code is likely to emerge within 24-48 hours of this disclosure, if it hasn't already been developed by threat actors who monitor WordPress plugin changelogs for security fixes.
Detection & Response
SIGMA Rules
The following Sigma rules detect both the exploitation attempt and post-exploitation activities commonly associated with PHP Object Injection attacks on WordPress.
---
title: Gravity Forms PHP Object Injection - POST Request with Serialized Payload
id: 8f4e2d91-6b3a-4c85-9e17-5f8a2b9c4d1e
status: experimental
description: Detects HTTP POST requests to WordPress containing serialized PHP object patterns indicative of CVE-2026-103039 exploitation attempts against Gravity Forms.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-103039
- https://www.gravityforms.com/
author: Security Arsenal
date: 2026/02/09
tags:
- attack.initial_access
- attack.t1190
logsource:
category: webserver
product: apache
detection:
selection:
c-uri|contains:
- '/wp-admin/admin-ajax.php'
- '/index.php'
cs-method: 'POST'
c-uri-query|contains:
- 'O:8:'
- 'O:9:'
- 'O:10:'
- 'a:1:{'
- 's:4:"'
filter:
c-uri-query|contains: 'action=gform_submit'
condition: selection and not filter
falsepositives:
- Legitimate Gravity Forms submissions with complex field data
- Other WordPress plugins that use serialized data
level: high
---
title: PHP Process Spawning Command Shell - WordPress Context
id: 3c7a9f52-8d1e-4b96-a3c4-7e2f1d8b5a9c
status: experimental
description: Detects PHP processes spawning command shells or system utilities, commonly observed after successful PHP Object Injection exploitation in WordPress environments.
references:
- https://attack.mitre.org/techniques/T1059/004/
author: Security Arsenal
date: 2026/02/09
tags:
- attack.execution
- attack.t1059.004
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentCommandLine|contains:
- 'php'
- 'apache2'
- 'httpd'
- 'nginx'
- 'php-fpm'
selection_child:
CommandLine|contains:
- '/bin/sh'
- '/bin/bash'
- 'curl'
- 'wget'
- 'nc '
- 'netcat'
- 'python'
- 'perl'
- '/tmp/'
- 'base64'
condition: selection_parent and selection_child
falsepositives:
- Legitimate WordPress maintenance scripts
- WP-CLI operations
- Backup plugins executing system commands
level: high
---
title: Suspicious File Creation in WordPress Uploads Directory
id: 9e1d4b83-2f7c-4a58-b9e6-3c8f5a1d7e2b
status: experimental
description: Detects creation of PHP files in WordPress uploads directory, a common post-exploitation activity following successful Gravity Forms compromise.
references:
- https://attack.mitre.org/techniques/T1105/
author: Security Arsenal
date: 2026/02/09
tags:
- attack.persistence
- attack.t1505.003
logsource:
category: file_event
product: linux
detection:
selection_path:
TargetFilename|contains:
- '/wp-content/uploads/'
- '/var/www/html/wp-content/uploads/'
selection_ext:
TargetFilename|endswith:
- '.php'
- '.phtml'
- '.php5'
- '.php7'
selection_action:
- 'create'
- 'write'
- 'modify'
condition: selection_path and selection_ext and selection_action
falsepositives:
- Plugin/theme updates that write to uploads directory
- Legitimate file uploads with PHP handler misconfiguration
level: medium
KQL Hunt Query (Microsoft Sentinel / Defender)
This hunt query identifies potential exploitation attempts and post-exploitation activity. It assumes you're ingesting web server logs, process execution events, or endpoint data into Sentinel.
// Hunt for Gravity Forms exploitation attempts and post-exploitation activity
// Time range: Last 7 days
union isfuzzy=true
(
// Web server logs - exploitation attempts
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where DeviceVendor =~ "Apache" or DeviceProduct contains "nginx"
| where RequestMethod == "POST"
| where RequestURL has_any ("admin-ajax.php", "index.php", "wp-json")
| where AdditionalExtensions has_any ("O:8:", "O:9:", "a:1:{", "serialize")
| where AdditionalExtensions has "gform"
| project TimeGenerated, SourceIP, RequestURL, RequestMethod, AdditionalExtensions, DeviceAction
),
(
// Process execution - PHP spawning shells
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("php", "php-fpm", "apache2", "httpd", "nginx")
| where FileName in~ ("sh", "bash", "curl", "wget", "nc", "python", "perl")
| where ProcessCommandLine has_any ("/tmp/", "base64", "eval", "system", "exec")
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, ProcessCommandLine, AccountName, RemoteIP
),
(
// Syslog - suspicious file writes in uploads
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has "wp-content/uploads"
| where SyslogMessage has_any (".php", ".phtml")
| where SyslogMessage has_any ("create", "write", "modify")
| project TimeGenerated, Computer, ProcessName, SyslogMessage, HostIP
),
(
// Network connections from web server processes
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("php", "php-fpm", "apache2", "httpd")
| where RemotePort in (80, 443, 8080)
| where RemoteIPType == "Public"
| where RemoteUrl !has_any ("wordpress.org", "wordpress.com", "gravityforms.com")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, RemoteIP, RemotePort, RemoteUrl
)
| order by TimeGenerated desc
Velociraptor VQL Hunt Artifact
This VQL artifact hunts for indicators of compromise related to Gravity Forms exploitation, including suspicious processes, recently modified PHP files in uploads, and network connections from web server processes.
-- CVE-2026-103039: Hunt for Gravity Forms exploitation artifacts
-- Identifies suspicious processes, file modifications, and network activity
LET suspicious_processes = SELECT
Pid,
Name,
CommandLine,
Exe,
Username,
CreateTime,
PPid
FROM pslist()
WHERE
(Name =~ "php|apache|httpd|nginx" AND CommandLine =~ "sh|bash|curl|wget|nc |python|perl")
OR (CommandLine =~ "/tmp/|base64|eval|system\(|exec\(")
LET uploads_php_files = SELECT
FullPath,
Size,
Mtime,
Ctime
FROM glob(globs="/var/www/html/wp-content/uploads/**/*.php")
WHERE Mtime > (now() - (7 * 24 * 60 * 60))
ORDER BY Mtime DESC
LET suspicious_connections = SELECT
Pid,
Name,
LocalAddr,
LocalPort,
RemoteAddr,
RemotePort,
State
FROM netstat()
WHERE
Name =~ "php|apache|httpd|nginx"
AND State =~ "ESTABLISHED"
AND RemoteAddr !~ "^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|127\.)"
SELECT
'Suspicious Process' AS IndicatorType,
Pid AS PID,
Name AS ProcessName,
CommandLine AS CommandLine,
Username AS Username,
CreateTime AS Timestamp,
NULL AS FilePath,
NULL AS RemoteAddress
FROM suspicious_processes
UNION ALL
SELECT
'Modified PHP File' AS IndicatorType,
NULL AS PID,
NULL AS ProcessName,
NULL AS CommandLine,
NULL AS Username,
Mtime AS Timestamp,
FullPath AS FilePath,
NULL AS RemoteAddress
FROM uploads_php_files
UNION ALL
SELECT
'Outbound Connection' AS IndicatorType,
Pid AS PID,
Name AS ProcessName,
NULL AS CommandLine,
NULL AS Username,
NULL AS Timestamp,
NULL AS FilePath,
RemoteAddr + ':' + RemotePort AS RemoteAddress
FROM suspicious_connections
ORDER BY Timestamp DESC
Remediation Script
This Bash script checks for vulnerable Gravity Forms installations, verifies the version, and provides remediation guidance. Run this on your WordPress servers or use it as part of your vulnerability scanning process.
#!/bin/bash
# CVE-2026-103039: Gravity Forms Vulnerability Scanner and Remediation Script
# Checks for vulnerable Gravity Forms installations on WordPress sites
set -e
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
VULNERABLE_VERSION="3.1.2"
PATCHED_VERSION="3.1.3"
echo "================================================"
echo "Gravity Forms CVE-2026-103039 Detection Script"
echo "================================================"
echo ""
# Function to compare version numbers
version_compare() {
if [[ $1 == $2 ]]; then
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do
ver1[i]=0
done
for ((i=0; i<${#ver1[@]}; i++)); do
if [[ -z ${ver2[i]} ]]; then
ver2[i]=0
fi
if ((10#${ver1[i]} > 10#${ver2[i]})); then
return 1
fi
if ((10#${ver1[i]} < 10#${ver2[i]})); then
return 2
fi
done
return 0
}
# Search common WordPress installation paths
WP_PATHS=(
"/var/www/html"
"/var/www/wordpress"
"/var/www/vhosts"
"/home/*/public_html"
"/usr/share/nginx/html"
)
VULNERABLE_SITES=()
PATCHED_SITES=()
NO_GRAVITY_FORMS=()
echo "Scanning for WordPress installations..."
echo ""
for wp_path in "${WP_PATHS[@]}"; do
# Expand wildcards
for site_path in $wp_path; do
if [ ! -d "$site_path" ]; then
continue
fi
# Check if this is a WordPress installation
if [ ! -f "$site_path/wp-config.php" ]; then
continue
fi
echo "Found WordPress installation: $site_path"
# Check for Gravity Forms plugin
GF_PATH="$site_path/wp-content/plugins/gravityforms"
if [ ! -d "$GF_PATH" ]; then
echo -e " ${GREEN}[OK]${NC} Gravity Forms not installed"
NO_GRAVITY_FORMS+=("$site_path")
continue
fi
# Get Gravity Forms version
GF_VERSION=$(grep "Version:" "$GF_PATH/gravityforms.php" | head -1 | awk '{print $2}' | tr -d '\r\n ')
if [ -z "$GF_VERSION" ]; then
echo -e " ${YELLOW}[WARNING]${NC} Could not determine Gravity Forms version"
continue
fi
echo " Gravity Forms version: $GF_VERSION"
# Compare versions
version_compare "$GF_VERSION" "$VULNERABLE_VERSION"
result=$?
if [ $result -eq 0 ] || [ $result -eq 2 ]; then
echo -e " ${RED}[VULNERABLE]${NC} Gravity Forms $GF_VERSION is affected by CVE-2026-103039"
VULNERABLE_SITES+=("$site_path:$GF_VERSION")
else
echo -e " ${GREEN}[PATCHED]${NC} Gravity Forms $GF_VERSION is not vulnerable"
PATCHED_SITES+=("$site_path:$GF_VERSION")
fi
echo ""
done
done
echo ""
echo "================================================"
echo "SCAN RESULTS"
echo "================================================"
echo ""
echo -e "${RED}VULNERABLE SITES (${#VULNERABLE_SITES[@]}):${NC}"
for site in "${VULNERABLE_SITES[@]}"; do
echo " - $site"
done
echo ""
echo -e "${GREEN}PATCHED SITES (${#PATCHED_SITES[@]}):${NC}"
for site in "${PATCHED_SITES[@]}"; do
echo " - $site"
done
echo ""
echo "Sites without Gravity Forms: ${#NO_GRAVITY_FORMS[@]}"
echo ""
if [ ${#VULNERABLE_SITES[@]} -gt 0 ]; then
echo -e "${RED}CRITICAL: Immediate action required!${NC}"
echo ""
echo "REMEDIATION STEPS:"
echo "1. Update Gravity Forms to version 3.1.3 or later immediately"
echo "2. Update via WordPress admin: Plugins > Installed Plugins > Gravity Forms > Update"
echo "3. Or download from: https://www.gravityforms.com/my-account/downloads/"
echo "4. After updating, review site logs for signs of compromise"
echo "5. Check wp-content/uploads for suspicious PHP files"
echo "6. Review user accounts for unauthorized administrator accounts"
echo ""
echo "For WP-CLI users:"
for site in "${VULNERABLE_SITES[@]}"; do
site_path=$(echo $site | cut -d: -f1)
echo " cd $site_path && wp plugin update gravityforms"
done
exit 1
else
echo -e "${GREEN}No vulnerable Gravity Forms installations detected.${NC}"
exit 0
fi
Remediation
Immediate Actions
Priority 1 - Update Gravity Forms (URGENT):
-
Update Gravity Forms to version 3.1.3 or later immediately. This version includes the fix that properly uses
GFCommon::maybe_unserialize()withallowed_classes => falsethroughout the codebase. -
Update methods:
- WordPress Admin: Navigate to Plugins > Installed Plugins > Gravity Forms > Update Now
- Manual Update: Download from Gravity Forms Account Page and upload via FTP/SFTP
- WP-CLI: Run
wp plugin update gravityformsfrom your WordPress root directory
-
Verify the update: Confirm the version by checking the plugin header or via WP-CLI:
wp plugin get gravityforms --field=version
Priority 2 - Assess Impact:
-
Inventory all WordPress sites running Gravity Forms across your organization, including:
- Production websites
- Staging/development environments
- Legacy sites that may still be publicly accessible
- Multisite installations (check each subsite)
-
Identify forms using Post Custom Fields with List input type:
- Review each Gravity Form configuration
- Export form configurations and search for Post Custom Fields
- Document which forms have content templates enabled
-
Review web server logs for suspicious POST requests to forms endpoints, particularly:
- Requests containing serialized PHP object patterns (O:8:, O:9:, a:1:{)
- POST requests to
/wp-admin/admin-ajax.phpor/index.phpwith gform parameters - Requests from unusual IP addresses or user agents
Detection and Forensics
Check for signs of compromise:
-
File integrity:
- Scan
wp-content/uploads/directories for PHP files (legitimate uploads should not include executable PHP) - Check for recently modified core WordPress files
- Look for unexpected files in the root WordPress directory
- Scan
-
Database review:
- Check
wp_userstable for unauthorized administrator accounts - Review
wp_optionsfor suspicious siteurl or home values - Examine
wp_postsfor injected content or malicious shortcodes
- Check
-
User accounts:
- Audit all administrator and editor accounts
- Review recent login activity (Wordfence or similar security plugin logs)
- Check for accounts with no associated author content
-
Outbound connections:
- Review server egress traffic from web server processes
- Check for connections to known malicious IPs or unusual external destinations
Additional Hardening
Post-remediation hardening steps:
-
Web Application Firewall (WAF):
- Deploy or update WAF rules to block requests containing serialized PHP object patterns
- Consider Cloudflare, Sucuri, or Wordfence Premium for managed WAF protection
- Implement rate limiting on form submission endpoints
-
Disable unused features:
- Remove or disable Post Custom Fields if not required
- Disable content templates on form fields unless specifically needed
- Review and minimize installed WordPress plugins
-
File system hardening:
- Prevent PHP execution in uploads directory:
Code
- Prevent PHP execution in uploads directory:
Add to wp-content/uploads/.htaccess
<Files *.php> deny from all
- Or for nginx:
```
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
-
Principle of least privilege:
- Ensure WordPress file permissions follow security best practices
- Run PHP-FPM with dedicated user accounts per site
- Implement SELinux or AppArmor policies to restrict web server capabilities
-
Monitoring and logging:
- Enable detailed web server access and error logging
- Deploy file integrity monitoring on WordPress directories
- Implement endpoint detection and response (EDR) on WordPress servers
- Configure alerting for PHP process spawning shells or unexpected network connections
Timeline and Vendor Information
- Vulnerability Disclosed: February 8, 2026
- Patch Released: February 8, 2026 (Gravity Forms 3.1.3)
- NVD Publication: February 9, 2026
- Vendor Advisory: Gravity Forms Security Advisories
- CISA KEV Status: Not yet added as of February 9, 2026
Note: Given the critical severity (CVSS 9.8) and unauthenticated nature of this vulnerability, we recommend treating this as an emergency patch. Do not wait for your regular patch cycle. Organizations should complete remediation within 24 hours of this disclosure.
Long-term Recommendations
-
Implement a vulnerability management program that tracks WordPress plugins and themes as distinct assets requiring regular updates.
-
Subscribe to security notifications:
- Gravity Forms security mailing list
- WordPress security announcements
- Wordfence Threat Intelligence
- CISA Known Exploited Vulnerabilities catalog
-
Consider managed WordPress hosting that includes automatic plugin updates and security monitoring as part of the service.
-
Regular security assessments:
- Quarterly penetration testing of public-facing WordPress sites
- Annual comprehensive security audits
- Continuous security monitoring through a managed security services provider (MSSP)
-
Incident response readiness:
- Maintain offline backups of WordPress sites and databases
- Document incident response procedures specific to WordPress compromises
- Test restoration procedures regularly
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.