Newsletter – Send awesome emails from WordPress <= 9.1.0 - Cross-Site Request Forgery to Newsletter Unsubscription
Description
The Newsletter – Send awesome emails from WordPress plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 9.1.0. This is due to missing or incorrect nonce validation on the hook_newsletter_action() function. This makes it possible for unauthenticated attackers to unsubscribe newsletter subscribers via a forged request granted they can trick a logged-in user 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
<=9.1.0Source Code
WordPress.org SVNThis research plan focuses on exploiting a Cross-Site Request Forgery (CSRF) vulnerability in the **Newsletter** plugin (<= 9.1.0). The vulnerability resides in the `hook_newsletter_action()` function, which processes newsletter-related actions like unsubscription without sufficient nonce validation…
Show full research plan
This research plan focuses on exploiting a Cross-Site Request Forgery (CSRF) vulnerability in the Newsletter plugin (<= 9.1.0). The vulnerability resides in the hook_newsletter_action() function, which processes newsletter-related actions like unsubscription without sufficient nonce validation.
1. Vulnerability Summary
- Vulnerability: Cross-Site Request Forgery (CSRF)
- Location:
newsletter/includes/newsletter.php(or similar core file wherehook_newsletter_actionis defined). - Cause: The plugin registers a hook (typically on
initorwp_loaded) calledhook_newsletter_action. This function parses thena(Newsletter Action) parameter from the request. Whennais set tou(unsubscribe), it processes the unsubscription logic. In affected versions, this logic lacks a valid WordPress nonce check, allowing an attacker to trigger the unsubscription of any subscriber if they can guess or obtain that subscriber's unique key (nk).
2. Attack Vector Analysis
- Endpoint: The WordPress frontend (any URL, but typically the root
/). - Hook:
initorwp_loaded. - Action Parameter:
na=u(triggers the unsubscription flow). - Identity Parameter:
nk(Newsletter Key - a unique hash for each subscriber). - Authentication: Unauthenticated.
- Preconditions: The attacker must know the subscriber's unique key (
nk). While this is a secret token, the vulnerability is a CSRF because if a user (subscriber) clicks a malicious link or visits a page with a hidden request to/?na=u&nk=[user_key], the plugin will process the unsubscription without confirming the user's intent via a nonce.
3. Code Flow
- Entry Point:
Newsletter::hook_newsletter_action()is called during the WordPressinitcycle. - Action Identification: The function checks
$_REQUEST['na']. If$_REQUEST['na'] == 'u', it enters the unsubscription logic. - Subscriber Identification: It retrieves the subscriber using
$_REQUEST['nk']. - Vulnerable Point: The code proceeds to update the subscriber status in the database (typically to
'U'for unsubscribed) without callingwp_verify_nonce()orcheck_admin_referer(). - Sink: The database is updated via
$wpdb->updateon the{prefix}newslettertable.
4. Nonce Acquisition Strategy
According to the vulnerability description, this is a missing or incorrect nonce validation.
- If the validation is missing: No nonce is required. The exploit can be sent as a direct request.
- If the validation is incorrect (e.g., checking a nonce that is never generated or using a weak check): The exploit will likely succeed by simply omitting the nonce or providing a dummy value.
Since the goal is unauthenticated CSRF against a subscriber action, and these actions are intended to be performed via email links (which traditionally use the nk token instead of WP nonces), the vulnerability likely stems from the plugin failing to implement a "Confirm Unsubscribe" page that uses a WP nonce to verify the action was intentional when triggered via the web.
5. Exploitation Strategy
- Preparation: Identify a target subscriber's
nkkey. - Attack: Use
http_requestto simulate a victim's browser being forced to visit the unsubscription URL. - Request Details:
- Method:
GET(most common for link-based unsubscription) orPOST. - URL:
http://localhost:8080/?na=u&nk=[SUBSCRIBER_NK] - Headers:
Content-Type: application/x-www-form-urlencoded
- Method:
- Redirection: The plugin usually redirects to a "Goodbye" page or a confirmation message.
6. Test Data Setup
To demonstrate the vulnerability, we must first create a subscriber and extract their secret key.
- Create Subscriber:
wp eval "NewsletterSubscription::instance()->subscribe(['email'=>'victim@example.com', 'status'=>'C']);" - Extract NK:
# Get the 'nk' (access key) for the created email NK=$(wp db query "SELECT token FROM wp_newsletter WHERE email='victim@example.com'" --skip-column-names) echo $NK - Check Initial Status:
wp db query "SELECT status FROM wp_newsletter WHERE email='victim@example.com'"
7. Expected Results
- The
http_requestshould return a200 OKor a302 Redirect. - The subscriber status for
victim@example.comin thewp_newslettertable should change from'C'(Confirmed) to'U'(Unsubscribed). - No "Invalid Nonce" or "Security Check Failed" error should appear.
8. Verification Steps
After executing the exploit, verify the database state using WP-CLI:
# The status should be 'U'
wp db query "SELECT email, status FROM wp_newsletter WHERE email='victim@example.com'"
9. Alternative Approaches
If the nk parameter alone is not enough, the plugin might require an ni (Subscriber ID) parameter as well.
- Alternative URL:
/?na=u&nk=[NK]&ni=[ID] - Extraction:
ID=$(wp db query "SELECT id FROM wp_newsletter WHERE email='victim@example.com'" --skip-column-names)
If the plugin implements a partial check, try adding a fake nonce:
/?na=u&nk=[NK]&_wpnonce=1234567890
If the action is handled via admin-ajax.php (less likely for unsubscription links but possible):
- Action:
newsletter - Data:
action=newsletter&na=u&nk=[NK]
Summary
The Newsletter plugin is vulnerable to Cross-Site Request Forgery (CSRF) because the `hook_newsletter_action` function fails to validate nonces for the unsubscription action. An unauthenticated attacker can force a subscriber to be unsubscribed by tricking them into clicking a link that includes their unique newsletter token.
Vulnerable Code
// newsletter/includes/newsletter.php public function hook_newsletter_action() { $action = $_REQUEST['na'] ?? ''; if ($action === 'u') { $user_key = $_REQUEST['nk'] ?? ''; // The code retrieves the user by their unique token (nk) $user = $this->get_user_by_token($user_key); if ($user) { // VULNERABILITY: No wp_verify_nonce() or confirmation check exists here. // The unsubscription is processed immediately upon a GET/POST request. $this->unsubscribe_user($user->id); $this->show_unsubscription_message(); die(); } } }
Security Fix
@@ -540,6 +540,11 @@ if ($action === 'u') { + // Add nonce verification or a confirmation step to prevent CSRF + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'newsletter_unsubscribe' ) ) { + $this->render_unsubscribe_confirmation_page(); + die(); + } $user_key = $_REQUEST['nk'] ?? ''; $user = $this->get_user_by_token($user_key);
Exploit Outline
The exploit targets the unsubscription logic in the plugin's core initialization. 1. **Endpoint**: The attack targets the WordPress frontend home page or any URL where the plugin's `init` hooks run. 2. **Payload**: A GET request with the parameters `na=u` (Newsletter Action: Unsubscribe) and `nk=[SUBSCRIBER_KEY]`. 3. **Authentication**: The attacker does not need to be authenticated. However, they must know the victim's unique `nk` (Newsletter Key). 4. **Methodology**: The attacker crafts a link or an auto-loading image/script (e.g., `<img src="http://target.com/?na=u&nk=xyz123">`) and tricks a subscriber into loading it. Since there is no nonce check, the plugin identifies the user via the `nk` parameter and changes their status to 'Unsubscribed' in the database without user consent.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.