CVE-2025-14782

Forminator Forms – Contact Form, Payment Form & Custom Form Builder <= 1.49.1 - Missing Authorization to Authenticated (Forminator User+) CSV Export

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.49.2
Patched in
1d
Time to patch

Description

The Forminator Forms – Contact Form, Payment Form & Custom Form Builder plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 1.49.1 via the 'listen_for_csv_export' function. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with access to the Forminator dashboard, to export sensitive form submission data including personally identifiable information.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=1.49.1
PublishedJanuary 8, 2026
Last updatedJanuary 9, 2026
Affected pluginforminator

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2025-14782 ## 1. Vulnerability Summary The Forminator Forms plugin for WordPress is vulnerable to an authorization bypass in versions up to 1.49.1. The flaw exists in the `listen_for_csv_export` function, which handles requests to export form submission data as CS…

Show full research plan

Exploitation Research Plan - CVE-2025-14782

1. Vulnerability Summary

The Forminator Forms plugin for WordPress is vulnerable to an authorization bypass in versions up to 1.49.1. The flaw exists in the listen_for_csv_export function, which handles requests to export form submission data as CSV files. The function fails to perform a formal capability check (e.g., current_user_can()) to ensure that the authenticated user requesting the export has the appropriate permissions (like manage_options or forminator_edit_forms). Consequently, any authenticated user who can access the WordPress admin dashboard (even those with minimal permissions, if they can obtain the necessary nonce) can trigger an export of sensitive submission data containing PII.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin.php (triggered via admin_init hook)
  • Action/Trigger: The presence of the forminator_export query parameter.
  • Vulnerable Function: Forminator_Admin_Submissions_Page::listen_for_csv_export()
  • Authentication: Authenticated. The CVSS indicates "Low Privilege" (PR:L). While the description says "Forminator User+", many sites allow low-privilege users (Contributor/Subscriber) to access certain admin-facing components, or the capability check is missing entirely for any logged-in user.
  • Preconditions:
    1. At least one form must exist with submissions.
    2. A valid nonce for the action forminator_export_submissions must be obtained (if enforced).

3. Code Flow

  1. Entry Point: The plugin registers listen_for_csv_export to the admin_init hook in admin/pages/class-submissions-page.php.
  2. Trigger: When a request is made to /wp-admin/admin.php (or any admin page), admin_init fires.
  3. Parameter Check: listen_for_csv_export() checks for isset( $_GET['forminator_export'] ).
  4. Processing:
    • It retrieves form_id and form_type from $_GET.
    • It verifies the nonce using wp_verify_nonce( $_GET['_wpnonce'], 'forminator_export_submissions' ).
    • The Flaw: It proceeds to call Forminator_Export::export_submissions() without verifying if the current user has the authority to view submissions for the requested form_id.
  5. Sink: The plugin generates a CSV file, sets the HTTP headers for download, and calls die(), sending the PII data to the attacker's browser.

4. Nonce Acquisition Strategy

The export functionality relies on a nonce named forminator_export_submissions.

  1. Shortcode Identification: Forminator forms are rendered via the [forminator_form id="ID"] shortcode.
  2. Page Creation:
    wp post create --post_type=page --post_status=publish --post_title="Form Page" --post_content='[forminator_form id="123"]'
    
  3. Extraction:
    While the export nonce is usually on the admin submissions page, Forminator often localizes nonces into the forminator_data or forminator_admin_data JS objects.
    • Navigate to the Forminator Submissions page as a low-privilege user (if accessible) OR check if the nonce is leaked on the Forminator Dashboard.
    • If the user cannot access the Submissions page directly, check if the nonce is generated in the wp_localize_script for the main Forminator Dashboard:
      • browser_navigate("/wp-admin/admin.php?page=forminator")
      • browser_eval("window.forminatorData?.nonces?.export_submissions")
  4. Bypass Check: Verify if listen_for_csv_export actually checks the nonce return value correctly. If it uses check_ajax_referer with die=false or fails to check wp_verify_nonce, the nonce may not be strictly required.

5. Exploitation Strategy

  1. Setup: Log in as a user with the lowest possible role that can access the WordPress backend (e.g., Contributor).
  2. Form Discovery: Identify a valid form_id. Since IDs are sequential integers, they can be discovered via the frontend or by brute-forcing.
  3. Request Construction:
    • Method: GET
    • URL: http://localhost:8080/wp-admin/admin.php
    • Parameters:
      • page: forminator-entries
      • form_id: [TARGET_FORM_ID]
      • form_type: custom_form
      • forminator_export: 1
      • _wpnonce: [EXTRACTED_NONCE]
  4. HTTP Request via http_request tool:
    {
      "method": "GET",
      "url": "http://localhost:8080/wp-admin/admin.php?page=forminator-entries&form_id=1&form_type=custom_form&forminator_export=1&_wpnonce=a1b2c3d4e5"
    }
    
  5. Capture: The response should have Content-Type: text/csv and contain the form's submission data.

6. Test Data Setup

  1. Create Form: Use WP-CLI or the UI to create a simple contact form.
  2. Submit Data: Perform several submissions to the form as an anonymous user to populate the database with "PII" (e.g., Name: Victim, Email: victim@example.com, Message: Secret data).
  3. Create Attacker User:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    
  4. Grant Minimal Permissions (if necessary): If the plugin requires "Forminator User" access, use the Forminator settings (Permissions tab) to allow the 'contributor' role to see the Dashboard (but not necessarily Submissions).

7. Expected Results

  • A successful exploit will return an HTTP 200 response with Content-Disposition: attachment; filename=...csv.
  • The body of the response will contain a CSV-formatted list of all submissions for the specified form_id, including user-submitted names, emails, and any other form fields.

8. Verification Steps

  1. Check Response Headers: Verify Content-Type is text/csv.
  2. Check Response Body: Confirm the presence of the strings submitted during the "Test Data Setup" phase (e.g., victim@example.com).
  3. Database Comparison:
    wp db query "SELECT * FROM wp_forminator_form_entry_meta WHERE entry_id IN (SELECT entry_id FROM wp_forminator_form_entry WHERE form_id = 1);"
    
    Compare the CLI output with the CSV data received via the HTTP request.

9. Alternative Approaches

  • Different Form Types: If custom_form fails, try form_type=quiz or form_type=poll.
  • Nonce Bypass: Try the request without the _wpnonce parameter to see if the verification is conditionally skipped.
  • Dashboard Access: If the Contributor cannot access /wp-admin/admin.php?page=forminator-entries, attempt the request via admin-ajax.php if the plugin uses a generic admin_init listener that doesn't check the specific page slug.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Forminator plugin for WordPress is vulnerable to an authorization bypass because the `listen_for_csv_export` function fails to perform a formal capability check (such as current_user_can). This allows authenticated users with access to the Forminator dashboard to trigger a CSV export of form submission data, exposing sensitive PII.

Vulnerable Code

// admin/pages/class-submissions-page.php

public function listen_for_csv_export() {
    if ( isset( $_GET['forminator_export'] ) ) {
        $form_id   = isset( $_GET['form_id'] ) ? intval( $_GET['form_id'] ) : 0;
        $form_type = isset( $_GET['form_type'] ) ? sanitize_text_field( $_GET['form_type'] ) : '';
        $nonce     = isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : '';

        if ( wp_verify_nonce( $nonce, 'forminator_export_submissions' ) ) {
            // Vulnerability: No capability check here before proceeding to export
            Forminator_Export::export_submissions( $form_id, $form_type );
            exit;
        }
    }
}

Security Fix

--- a/admin/pages/class-submissions-page.php
+++ b/admin/pages/class-submissions-page.php
@@ -123,6 +123,10 @@
         if ( wp_verify_nonce( $nonce, 'forminator_export_submissions' ) ) {
+			if ( ! current_user_can( 'forminator_edit_forms' ) ) {
+				return;
+			}
             Forminator_Export::export_submissions( $form_id, $form_type );
             exit;
         }

Exploit Outline

To exploit this vulnerability, an attacker must first obtain a valid nonce for the 'forminator_export_submissions' action, which is typically exposed in the WordPress admin dashboard for users granted 'Forminator User' permissions. Once the nonce is obtained, the attacker identifies a target Form ID (sequential integer). The attacker then makes a GET request to '/wp-admin/admin.php' with the parameters 'forminator_export=1', 'form_id=[TARGET_ID]', 'form_type=custom_form', and the '_wpnonce'. Because the plugin fails to check if the current user has the 'manage_options' or 'forminator_edit_forms' capability, the server will generate and return a CSV file containing all submission data for the specified form, including names, emails, and other potentially sensitive PII.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.