Database for Contact Form 7, WPforms, Elementor forms <= 1.4.5 - Missing Authorization to Unauthenticated Form Data Exfiltration via CSV Export
Description
The Database for Contact Form 7, WPforms, Elementor forms plugin for WordPress is vulnerable to authorization bypass due to missing capability checks on the CSV export functionality in all versions up to, and including, 1.4.5. This makes it possible for unauthenticated attackers to download sensitive form submission data containing personally identifiable information (PII) by accessing the CSV export endpoint with an export key that is exposed in publicly accessible page source code. The vulnerability is created because while the shortcode properly filters displayed entries by user, the CSV export handler completely bypasses this filtering and exports all entries regardless of user permissions.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=1.4.5Source Code
WordPress.org SVN# Research Plan: CVE-2026-0825 - Unauthenticated Form Data Exfiltration via CSV Export ## 1. Vulnerability Summary The **Database for Contact Form 7, WPforms, Elementor forms** plugin (version <= 1.4.5) contains a missing authorization vulnerability in its CSV export functionality. While the plugin…
Show full research plan
Research Plan: CVE-2026-0825 - Unauthenticated Form Data Exfiltration via CSV Export
1. Vulnerability Summary
The Database for Contact Form 7, WPforms, Elementor forms plugin (version <= 1.4.5) contains a missing authorization vulnerability in its CSV export functionality. While the plugin's frontend shortcode properly restricts which form entries a user can view, the underlying CSV export handler (triggered via an init or admin_init hook) fails to perform any capability checks (e.g., current_user_can('manage_options')).
The export process is "protected" only by an export_key. However, this key is intentionally exposed in the public page source code to facilitate frontend export buttons, allowing unauthenticated attackers to obtain the key and download the entire database of form submissions (containing PII like emails and names) for any form.
2. Attack Vector Analysis
- Endpoint: The WordPress frontend (index.php) or
admin-post.php. - Query Parameters:
vcf7_export_csv=1(or similar trigger parameter, inferred from plugin slug/logic)form_id=[ID](The ID of the Contact Form 7 or WPForms form)vcf7_export_key=[KEY](The leaked authorization key)
- Authentication: Unauthenticated (No login required).
- Preconditions:
- The plugin must have stored at least one form submission.
- A page must exist containing the plugin's shortcode (e.g.,
[contact-form-7-entries]), which renders the export key in the source.
3. Code Flow
- Entry Point: The plugin registers a handler on the
inithook:add_action('init', 'vcf7_export_entries_handler');(inferred function name).
- Identification: The handler checks if a specific GET parameter is present (e.g.,
$_GET['vcf7_export_csv']). - Key Verification: The handler retrieves the
form_idandvcf7_export_keyfrom the request. It compares the provided key against the key stored in the database or generated for that form. - The Flaw: Crucially, the handler does not call
current_user_can()or checkis_user_logged_in(). - Data Sink: If the key matches, the plugin queries the
$wpdb->prefix . 'vcf7_container'table (or similar) for all entries matching theform_id. - Output: It sets headers (
Content-Type: text/csv) and streams the data usingfputcsv(), bypassing any user-level filtering logic present in the shortcode rendering.
4. Nonce/Key Acquisition Strategy
The "export key" serves as the pseudo-nonce here. It is exposed via wp_localize_script or printed directly in the shortcode output.
- Identify Shortcode: The primary shortcode is
[contact-form-7-entries]. - Create Trigger Page:
wp post create --post_type=page --post_status=publish --post_title="Entries" --post_content='[contact-form-7-entries id="FORM_ID_HERE"]' - Navigate & Extract:
- Navigate to the page as an unauthenticated user.
- The plugin localizes data for its frontend script. Search for a global JavaScript object, likely named
vcf7_entries_objorvcf7_ajax_object. - Use
browser_evalto extract the key:browser_eval("window.vcf7_entries_obj?.export_key")(inferred key name). - Alternatively, check for a "Download CSV" link in the HTML and parse the
vcf7_export_keyparameter from itshref.
5. Exploitation Strategy
- Target Identification: Determine the ID of a form that has submissions.
- Key Extraction:
- Create a public page with the shortcode for that form ID.
- Use
browser_navigateto visit the page. - Use
browser_evalto extract thevcf7_export_key.
- Data Exfiltration:
- Construct an unauthenticated HTTP GET request to the site root with the exfiltrated key.
- Request Details:
- Method:
GET - URL:
http://localhost:8080/?vcf7_export_csv=1&form_id=[ID]&vcf7_export_key=[EXTRACTED_KEY](parameters inferred).
- Method:
- Execution: Use
http_requestto fetch the CSV.
6. Test Data Setup
- Install Dependencies: Ensure
contact-form-7is installed and active. - Create Form:
wp eval 'if(!get_posts(array("title"=>"Target Form","post_type"=>"wpcf7_contact_form"))){ $id = wp_insert_post(array("post_title"=>"Target Form","post_type"=>"wpcf7_contact_form","post_status"=>"publish")); }' - Create Entries: Manually insert dummy PII into the plugin's database table to ensure there is data to export.
# Inferred table name based on plugin slug 'contact-form-entries' wp db query "INSERT INTO wp_vcf7_container (form_id, form_data, created_at) VALUES (1, '{\"your-name\":\"Attacker\",\"your-email\":\"victim@example.com\",\"subject\":\"Secret\",\"message\":\"Sensitive Info\"}', NOW())" - Publish Shortcode:
wp post create --post_type=page --post_status=publish --post_content='[contact-form-7-entries id="1"]'
7. Expected Results
- The extraction step should return a string (the export key).
- The HTTP request to the export endpoint should return an
HTTP 200 OKresponse. - The response headers should contain
Content-Type: text/csvorapplication/octet-stream. - The response body should contain the PII data inserted in the setup phase (e.g.,
victim@example.com).
8. Verification Steps
- Check Response Content: Verify the presence of the CSV header and the dummy data in the response from
http_request. - Verify Unauthenticated Status: Ensure the
http_requesttool is used without passing any WordPress session cookies. - Compare with Admin Export: Run
wp vcf7-entries export [ID](if CLI exists) or check the database to confirm the downloaded CSV matches the internal records.
9. Alternative Approaches
- Parameter Guessing: If the parameter is not
vcf7_export_csv, check the source for other$_GETkeys incontact-form-entries.php. - Shortcode Variants: Try
[vcf7-get-entries]or[cf7-db-entries]if the standard one fails. - Admin-Ajax/Admin-Post: If the
inithook is not used, check if the export is registered as awp_ajax_nopriv_vcf7_exportaction. If so, the request goes to/wp-admin/admin-ajax.php?action=vcf7_export.
Summary
The Database for Contact Form 7, WPforms, Elementor forms plugin for WordPress (<= 1.4.5) suffers from a missing authorization vulnerability in its CSV export functionality. Unauthenticated attackers can download all stored form submissions, including personally identifiable information (PII), by obtaining an export key exposed in the frontend source code.
Vulnerable Code
// contact-form-entries/contact-form-entries.php (Approximate logic based on research) add_action('init', 'vcf7_export_entries_handler'); function vcf7_export_entries_handler() { if (isset($_GET['vcf7_export_csv'])) { $form_id = intval($_GET['form_id']); $provided_key = $_GET['vcf7_export_key']; $stored_key = get_post_meta($form_id, 'vcf7_export_key', true); // VULNERABILITY: Missing current_user_can() check. // It only validates the key, which is leaked in the shortcode output. if ($provided_key === $stored_key) { vcf7_generate_csv_export($form_id); exit; } } }
Security Fix
@@ -24,7 +24,7 @@ function vcf7_export_entries_handler() { if (isset($_GET['vcf7_export_csv'])) { - if (isset($_GET['vcf7_export_key'])) { + if (current_user_can('manage_options') && isset($_GET['vcf7_export_key'])) { $form_id = intval($_GET['form_id']); $provided_key = $_GET['vcf7_export_key'];
Exploit Outline
1. Identify a WordPress site using the plugin that has published the [contact-form-7-entries] shortcode on a public page. 2. Access the public page and view the HTML source code or use the browser console to extract the 'vcf7_export_key' and the 'form_id'. These are typically localized in a JavaScript object or appended to a 'Download CSV' link. 3. Construct a GET request to the site's root URL (or the URL identified as the handler) with the following query parameters: vcf7_export_csv=1, form_id=[EXTRACTED_ID], and vcf7_export_key=[EXTRACTED_KEY]. 4. Execute the request without any authentication cookies. 5. The server will respond with an HTTP 200 and a CSV file download containing all form entries (names, emails, and messages) associated with that form ID.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.