CVE-2025-12352 is a critical arbitrary file upload vulnerability in the Gravity Forms WordPress plugin, affecting all versions up to and including 2.9.20. Missing file type validation in the copy_post_image() function lets unauthenticated attackers upload PHP files to the server and, under certain server configurations, execute them remotely. The patched version is 2.9.21. Site owners running an affected version with the post creation feature and a Post Image field active should update immediately.
TLDR:
- Gravity Forms <= 2.9.20 has a CVSS 9.8 unauthenticated file upload flaw in
copy_post_image() - Exploitation requires
allow_url_fopen = Onand a post creation form with a Post Image field active - Update to Gravity Forms 2.9.21. The patch adds file type validation to
copy_post_image(), closing the upload path entirely - No confirmed in-the-wild exploitation of CVE-2025-12352 as of July 2026, but the unauthenticated attack path makes this a must-patch
- ZeroPath's AI-native platform traces source-to-sink data flow to detect CWE-434 class flaws in PHP codebases before they merge
What is CVE-2025-12352?
CVE-2025-12352 is an arbitrary file upload flaw in Gravity Forms, a widely deployed WordPress form plugin built by RocketGenius. The vulnerability carries a CVSS score of 9.8 (critical) and is classified under CWE-434 (Unrestricted Upload of File with Dangerous Type). It was reported by researcher Talal Nasraddeen and published in the Wordfence threat intelligence database on November 6, 2025.
The root cause is missing file type validation in the copy_post_image() function inside forms_model.php. When a form is configured with the post creation feature and a Post Image field active, this function copies files from external URLs to the WordPress uploads directory without checking the file extension or MIME type. An unauthenticated attacker can provide a URL that points to a PHP webshell and have it written to the uploads directory. Exploitation requires the server's PHP configuration to have allow_url_fopen = On; with that directive disabled, the remote fetch fails, and the file is never written. All Gravity Forms versions up to and including 2.9.20 are affected. RocketGenius patched the flaw in version 2.9.21 by adding file type validation to copy_post_image().
How the arbitrary file upload works
The vulnerable function
The copy_post_image() function accepts an external URL and copies its content to the WordPress uploads directory. No validation is performed on the file type or extension before writing the file. The relevant code path:
// forms_model.php copy_post_image() // lacks file type validation
This code is publicly visible in the Gravity Forms source on GitHub.
Preconditions for exploitation
Three conditions must align for exploitation to succeed:
- Gravity Forms version 2.9.20 or earlier is installed and active.
- A form with post creation active, and a Post Image field is present.
- The server's PHP configuration has
allow_url_fopen = On.
Attack path
- The attacker identifies a WordPress site with a Gravity Forms post creation form that includes a Post Image field.
- They host a malicious PHP file (such as a webshell) at an attacker-controlled URL.
- They submit the form with that URL as the Post Image value.
copy_post_image()fetches the file and writes it to the uploads directory without type validation.- If the web server executes files in the uploads directory, the attacker accesses the uploaded file directly to achieve remote code execution (RCE) on the server.
Why unauthenticated file upload flaws keep recurring in PHP form plugins
CVE-2025-12352 and CVE-2025-13407 (a second CVSS 9.8 file upload finding patched in Gravity Forms 2.9.24, disclosed one month later) share the same root cause: PHP's copy() function can fetch and write remote URLs when allow_url_fopen is active, and the code paths that call it in Gravity Forms lacked extension or MIME type checks.
WordPress PHP form plugins are a recurring target for CWE-434 for two structural reasons. First, they must handle file input from untrusted users by design, which means every new field type or feature that touches file handling is a potential introduction point for unrestricted file upload. Second, PHP's file functions (copy(), move_uploaded_file(), file_put_contents()) carry no built-in type enforcement. Validation is opt-in. A developer who writes a new image-copy helper without explicitly calling wp_check_filetype() or checking the extension against an allowlist ships a CWE-434 by default.
Client-side validation offers no protection here because the server fetches the file itself. The attacker never uploads anything through a browser form control. They provide a URL, the server fetches the file, and the result is saved to the uploads directory. This pattern completely bypasses client-side extension checks, which is why the exploit precondition is a server-side PHP setting, not anything the browser controls.

The class is being actively weaponized in 2026. Ninja Forms CVE-2026-0740, a CVSS 9.8 unauthenticated file upload flaw in the Ninja Forms File Uploads plugin, was under active exploitation by April 2026, with the Wordfence Firewall blocking over 118,600 exploit attempts targeting the vulnerability. The mechanics are the same: an AJAX handler that accepts file input with no type validation, no authentication required, and a PHP webshell upload achievable in a single request. CVE-2025-12352 shares that root cause. The Ninja Forms incidents confirm that the attack pattern is a live, recurring target in the WordPress ecosystem, not a theoretical edge case. ZeroPath's technical breakdown of CVE-2026-0740 explains how the same exploit class played out against that plugin.
Affected versions of Gravity Forms
Plugin | Affected versions | Patched version | Preconditions |
|---|---|---|---|
Gravity Forms (gravityforms) | All versions up to and including 2.9.20 | 2.9.21 |
|
Remediation steps for CVE-2025-12352
The primary fix is a one-step update. The steps below cover the patch itself, a server-side hardening measure, a post-incident audit check, and an optional WAF layer for sites that need defense-in-depth during the update.
- Update Gravity Forms to 2.9.21 or later. The patch adds file type validation to
copy_post_image(), blocking PHP and other executable files from being written to the uploads directory. This is the only complete fix. All other steps below are secondary mitigations. - Check
allow_url_fopenin your PHP configuration. Runphp -i | grep allow_url_fopenor inspect yourphp.ini. If it is set toOnand your application does not need to fetch remote URLs via PHP file functions, setting it toOffblocks the remote fetch step entirely. This removes the precondition that makes CVE-2025-12352 exploitable without patching, but it is not a substitute for the patch. - Audit
wp-content/uploadsfor unexpected PHP files. If your site ran an affected version with post creation and a Post Image field active, scan the uploads directory for files with.php,.phtml,.phar, or other executable extensions. A quick check:find wp-content/uploads -name "*.php" -o -name "*.phtml" -o -name "*.phar". Any result warrants immediate investigation. - Block PHP execution in the uploads directory at the server level. Whether or not you were exposed, preventing script execution in
wp-content/uploadsis sound hardening. For Nginx, deny PHP execution via a location block. For Apache, add an.htaccessrule in the uploads directory:
# wp-content/uploads/.htaccess <Files *.php> deny from all </Files>
This prevents uploaded PHP files from being served as executable scripts even if a future file-handling flaw writes one to the directory. Many managed WordPress hosts apply this by default; verify yours does.
CVE ID | Disclosed | Type | CVSS | Affected versions |
|---|---|---|---|---|
CVE-2025-12352 | Nov 6, 2025 | Unauthenticated arbitrary file upload (CWE-434) | 9.8 | <= 2.9.20 |
CVE-2025-13407 | Dec 3, 2025 | Unauthenticated arbitrary file upload (CWE-434) | 9.8 | <= 2.9.23.0 |
CVE-2026-3492 | Mar 10, 2026 | Authenticated stored XSS via form title (Subscriber+) | 6.4 | <= 2.9.28.1 |
CVE-2026-4394 | Apr 7, 2026 | Unauthenticated stored XSS via credit card sub-field | 6.1 | <= 2.9.30 |
CVE-2026-4406 | Apr 7, 2026 | Reflected XSS via | 4.7 | <= 2.9.30 |
CVE-2026-5109 / 5110 / 5111 / 5112 | May 1, 2026 | Unauthenticated stored XSS across product and repeater fields | 7.2 | <= 2.10.0 |
CVE-2026-5113 | May 1, 2026 | Unauthenticated stored XSS via consent field hidden input | 7.2 | <= 2.9.30 |
CVE-2026-48866 | Jun 1, 2026 | Unauthenticated arbitrary file deletion | 9.1 | <= 2.10.0.1 |
The July 2025 Gravity Forms supply-chain compromise
Beyond code-level CVEs, Gravity Forms experienced a supply chain compromise in July 2025. RocketGenius confirmed that versions 2.9.11.1 and 2.9.12 were tampered with by an external actor who injected malware directly into downloadable plugin packages. The malware blocked plugin updates and attempted to download a secondary payload designed to add rogue administrator accounts to affected WordPress sites, opening a persistent backdoor. RocketGenius released version 2.9.13 to fix the compromise. Site owners who ran either affected version should audit for unauthorized administrator accounts and apply the patch.
This is a separate attack class from the CVEs in the table above. Supply-chain tampering was exposed on two independent attack surfaces simultaneously: one through the update channel and one through any post-creation form with a Post Image field. The pattern reinforces a broader point about Gravity Forms' security posture in 2025 and 2026: site operators cannot treat each disclosure in isolation. The plugin's wide deployment and rich feature set make it a recurring target across multiple attack classes, and a single missed update can leave several independent exposure windows open simultaneously.
How ZeroPath detects arbitrary file upload vulnerabilities like CVE-2025-12352
A rule-based scanner pointed at PHP file-handling code flags every copy() call near user input. The result is a triage queue full of theoretical risks: paths that are never reachable from a public entry point, calls that have validation sitting two functions downstream, helpers that only run in authenticated admin context. Your team spends hours confirming what the scanner can't tell you, whether any of it is actually exploitable.
That's the problem ZeroPath solves for CWE-434-class vulnerabilities in PHP codebases. We trace the full source-to-sink data flow and confirm exploitability before anything reaches your findings dashboard. For a path to count as a finding, we map an external URL accepted as input, passed into copy() or a similar file-write function that lands in a web-accessible directory, with no extension check, MIME validation, or allowlist anywhere on that path. If the path is reachable and the guard is missing, the finding is recorded. If validation exists but sits in a separate code path that can be bypassed, the finding is still recorded.

This is where the architectural difference from pattern-matching scanners shows up. A rule-based scanner flags any copy() call near user input, regardless of whether that input can actually reach the call from a public entry point, or whether validation is present downstream. The result is a queue full of theoretical risks that take hours to triage. ZeroPath's multi-stage AI validation pass confirms exploitability before anything ships to the findings dashboard: data flows are traced, the entry point's authentication context is verified, and validation gaps are confirmed, not assumed. Per ZeroPath's internal data, this approach reduces false positives by up to 75%, so the CWE-434 findings that do surface are ones worth acting on.
For teams shipping custom PHP form-handling code, ZeroPath also lets you write a natural-language custom rule, something like "user-supplied URLs must be validated against an extension allowlist before being passed to file write functions." And that rule is checked on every full scan and every PR. If a developer adds a new image-copy helper without the validation step, the PR review flags it before it merges. When a CWE-434 finding is confirmed, ZeroPath generates a patch adding the missing extension check or allowlist validation and opens a pull request for review. After the fix merges, ZeroPath re-runs verification against the updated code and marks the issue resolved only once the remediation is confirmed, closing the loop instead of leaving findings in a backlog. CVE-2025-12352 is a reminder that this class of vulnerability appears in mature, widely deployed plugins. It shows up in custom codebases too, often in code paths that never got a formal security review because they looked like utility functions.
Final thoughts on CVE-2025-12352 and Gravity Forms security
CVE-2025-12352 is a reminder that missing input validation in a single function can open a path to full remote code execution on widely deployed infrastructure. The preconditions narrow real-world exposure, but the combination of an unauthenticated attack path and a CVSS score of 9.8 puts this squarely in the must-patch category. Sites running Gravity Forms should update to 2.9.21 now. If your team wants to catch these classes of vulnerability in your own PHP code before they reach production, book a ZeroPath demo to see the platform in action.
FAQ
Is CVE-2025-12352 being actively exploited?
No confirmed in-the-wild exploitation of CVE-2025-12352 has been publicly reported as of July 2026. The vulnerability is unauthenticated and carries a CVSS score of 9.8, making it a high-priority patch target regardless of current exploitation status.
Does updating Gravity Forms to 2.9.21 fully fix CVE-2025-12352?
Yes. Version 2.9.21 adds file type validation to the copy_post_image() function, closing the upload path that CVE-2025-12352 exploits.
What makes this vulnerability different from a standard file upload flaw?
The flaw is triggered by an external URL instead of a direct file upload. The server fetches and writes the file itself via PHP's copy() function, meaning client-side upload restrictions offer no protection against exploitation.
Can this be exploited on sites where allow_url_fopen is off?
No. The copy_post_image() function relies on PHP's copy() to fetch remote URLs. With allow_url_fopen = Off, the remote fetch fails and the file is never written to the server.



