Forminator Forms – Contact Form, Payment Form & Custom Form Builder <= 1.49.1 - Missing Authorization to Authenticated (Forminator User+) CSV Export
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:NTechnical Details
<=1.49.1Source Code
WordPress.org SVN# 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 viaadmin_inithook) - Action/Trigger: The presence of the
forminator_exportquery 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:
- At least one form must exist with submissions.
- A valid nonce for the action
forminator_export_submissionsmust be obtained (if enforced).
3. Code Flow
- Entry Point: The plugin registers
listen_for_csv_exportto theadmin_inithook inadmin/pages/class-submissions-page.php. - Trigger: When a request is made to
/wp-admin/admin.php(or any admin page),admin_initfires. - Parameter Check:
listen_for_csv_export()checks forisset( $_GET['forminator_export'] ). - Processing:
- It retrieves
form_idandform_typefrom$_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 requestedform_id.
- It retrieves
- 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.
- Shortcode Identification: Forminator forms are rendered via the
[forminator_form id="ID"]shortcode. - Page Creation:
wp post create --post_type=page --post_status=publish --post_title="Form Page" --post_content='[forminator_form id="123"]' - Extraction:
While the export nonce is usually on the admin submissions page, Forminator often localizes nonces into theforminator_dataorforminator_admin_dataJS 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_scriptfor the main Forminator Dashboard:browser_navigate("/wp-admin/admin.php?page=forminator")browser_eval("window.forminatorData?.nonces?.export_submissions")
- Bypass Check: Verify if
listen_for_csv_exportactually checks the nonce return value correctly. If it usescheck_ajax_refererwithdie=falseor fails to checkwp_verify_nonce, the nonce may not be strictly required.
5. Exploitation Strategy
- Setup: Log in as a user with the lowest possible role that can access the WordPress backend (e.g., Contributor).
- Form Discovery: Identify a valid
form_id. Since IDs are sequential integers, they can be discovered via the frontend or by brute-forcing. - Request Construction:
- Method:
GET - URL:
http://localhost:8080/wp-admin/admin.php - Parameters:
page:forminator-entriesform_id:[TARGET_FORM_ID]form_type:custom_formforminator_export:1_wpnonce:[EXTRACTED_NONCE]
- Method:
- HTTP Request via
http_requesttool:{ "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" } - Capture: The response should have
Content-Type: text/csvand contain the form's submission data.
6. Test Data Setup
- Create Form: Use WP-CLI or the UI to create a simple contact form.
- 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). - Create Attacker User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - 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
- Check Response Headers: Verify
Content-Typeistext/csv. - Check Response Body: Confirm the presence of the strings submitted during the "Test Data Setup" phase (e.g.,
victim@example.com). - Database Comparison:
Compare the CLI output with the CSV data received via the HTTP request.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);"
9. Alternative Approaches
- Different Form Types: If
custom_formfails, tryform_type=quizorform_type=poll. - Nonce Bypass: Try the request without the
_wpnonceparameter 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 viaadmin-ajax.phpif the plugin uses a genericadmin_initlistener that doesn't check the specific page slug.
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
@@ -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.